Stage 4

Biowulf2

cd /data/OGVFB_BG/scEiaD/2024_02_28/snakeout/hs111_mature_eye_full/NONneural_cells
mamba deactivate; mamba activate; bash ~/git/scEiaD_modeling/Snakemake.wrapper.sh ~/git/scEiaD_modeling/workflow/Snakefile ~/git/scEiaD_modeling/config/config_hs111_mature_eye_full__NONneural.yaml ~/git/scEiaD_modeling/config/cluster.json

Assess Output

library(tidyverse)
source('analysis_scripts.R')
obs_nonneural <- pull_obs('~/data/scEiaD_modeling/hs111_mature_eye_nonneural/hs111_mature_eye_20241001_full__NONneural2000hvg_200e_30l.obs.csv.gz', machine_label = 'MCT_scANVI_step4', drop_col = FALSE)
`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.
diff_nonneural <- pull_diff("~/data/scEiaD_modeling/hs111_mature_eye_nonneural/hs111_mature_eye_20241001_full__NONneural2000hvg_200e_30l.difftesting.leiden3.csv.gz")
'select()' returned 1:many mapping between keys and columns
Warning: Detected an unexpected many-to-many relationship between `x` and `y`.Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

Ratio (percentage) of labelled cell types for each leiden3 cluster

obs_nonneural$labels %>% 
  arrange(mCT) %>% 
  mutate(leiden3 = as.factor(leiden3)) %>% 
  DT::datatable(filter = 'top')

Mixed clusters

obs_nonneural$labels %>% 
  filter(grepl(",", mMCT)) %>% 
  arrange(mCT) %>% 
  mutate(leiden3 = as.factor(leiden3)) %>% 
  DT::datatable(filter = 'top')

UMAP Plots

obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = MCT_scANVI_step4), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(MCT_scANVI_step4) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = MCT_scANVI_step4, color = MCT_scANVI_step4)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none")


obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = MCT_scANVI), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none")


obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = as.factor(leiden3)), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey(), pals::alphabet(),pals::kelly(), pals::tableau20(), pals::watlington()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none")


obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = mMCT), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(mMCT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = mMCT, color = mMCT)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none")

NA
NA

hclust

Take pseudobulk values (at the cluster level) and hierarchically cluster them to ensure there aren’t any issues in either the overall structure (e.g. rod and cones are intersperse)d and/or to identify any potential mislabeled clusters

pb <- data.table::fread('~/data/scEiaD_modeling/hs111_mature_eye_nonneural/hs111_mature_eye_20241001_full__NONneural2000hvg_200e_30l.pseudoBulk.leiden3.csv.gz')
colnames(pb) <- gsub("\\.\\d+","",colnames(pb))
hvg <- data.table::fread('~/data/scEiaD_modeling/hs111_mature_eye_nonneural/hvg2000.csv.gz')[-1,]
rnames <- pb$V1
clust <- str_extract(rnames, '\\d+') %>% as.integer()
pb <- pb[,-1] %>% as.matrix()
row.names(pb) <- as.character(clust)
pb <- pb[as.character(obs_nonneural$labels$leiden3),]

pb_norm <- metamoRph::normalize_data(t(pb), sample_scale = 'cpm') %>% t() 
Sample CPM scaling
log1p scaling
# remove cell cycle genes
conv_table <- AnnotationDbi::select(org.Hs.eg.db::org.Hs.eg.db, 
                                    keys=gsub('\\.\\d+','',unique(colnames(pb_norm))),
                                    columns=c("ENSEMBL","SYMBOL", "MAP","GENENAME", "ENTREZID"), keytype="ENSEMBL")
'select()' returned 1:many mapping between keys and columns
cc_genes <- hvg %>% mutate(ENSEMBL = gsub("\\.\\d+","",V2)) %>% 
  left_join(conv_table, by = "ENSEMBL") %>% 
  mutate(cc_genes = case_when(SYMBOL %in% (Seurat::cc.genes.updated.2019 %>% unlist()) ~ TRUE)) %>% 
  filter(cc_genes) %>% pull(V2)
ribo_genes <- hvg %>% mutate(ENSEMBL = gsub("\\.\\d+","",V2)) %>% 
  left_join(conv_table, by = "ENSEMBL") %>% filter(grepl("^RPL|^RPS|^MT",SYMBOL)) %>% 
  pull(SYMBOL)

pb_norm <- pb_norm[,hvg$V2]
#pb_norm <- pb_norm[,hvg$V2[!hvg$V2 %in% c(cc_genes,ribo_genes)]]
# https://stats.stackexchange.com/questions/31565/compute-a-cosine-dissimilarity-matrix-in-r
sim <- pb_norm / sqrt(rowSums(pb_norm * pb_norm))
sim <- sim %*% t(sim)
D_sim <- as.dist(1 - sim)
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
hclust_sim <- hclust(D_sim, method = 'average')

hclust_sim$labels <- obs_nonneural$labels %>% pull(leiden3)

library(ggtree)
p <- ggtree(hclust_sim)
p$data <- p$data %>% left_join(obs_nonneural$labels, by = c("label" = "leiden3")) %>% 
  mutate(techRatio = round(techRatio, digits = ))
p + layout_dendrogram() +
  geom_tiplab(aes(label = paste(label, mMCT, studyCount, TotalCount, techRatio, sep = ' - '), color = mCT)) + 
  theme_dendrogram(plot.margin=margin(16,16,300,16)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  guides(color="none")



p <- ggtree(hclust_sim)
p$data <- p$data %>% left_join(obs_nonneural$labels %>% mutate(studies = case_when(studyCount ==1 ~ studies,
                                                                                   TRUE ~ "multiple")), by = c("label" = "leiden3")) 

p + layout_dendrogram() +
  geom_tiplab(aes(label = paste(label, mMCT, studies, sep = ' - '), color = mCT)) + 
  geom_tippoint(aes(shape = studies), size= 3) +
  theme_dendrogram(plot.margin=margin(16,16,300,16)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  guides(color="none")




p <- ggtree(hclust_sim)
p$data <- p$data %>% left_join(obs_nonneural$labels %>% mutate(studies = case_when(studyRatio ==1 ~ studiesRatio,
                                                                                   TRUE ~ "multiple")), by = c("label" = "leiden3")) 

p + layout_dendrogram() +
  geom_tiplab(aes(label = paste(label, mMCT, studies, sep = ' - '), color = mCT)) + 
  geom_tippoint(aes(shape = studies), size= 3) +
  theme_dendrogram(plot.margin=margin(16,16,300,16)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  guides(color="none")

NA
NA

Call CT

rpe markers

all rpe clusters express high BEST1/RPE65

tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("rpe|mueller|melano",mMCT)) %>% 
  left_join(conv_table) %>% 
  filter(SYMBOL %in% c("BEST1","RPE65", "PLD5","NEDD4L","OTX2","OPCML","INT1")) %>% 
  mutate(base = as.character(base),
         base = case_when(grepl("rpe", mMCT) ~ paste0(base, ' - ', mMCT),
                          TRUE ~ base)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        top_annotation = ha_column,
                        name = 'logFoldChange')


# hr_nonneural$`rpe (otx2-, opcml-)` <- c(33)
# hr_nonneural$`rpe (otx2+, opcml-)` <- c(77,113)
# hr_nonneural$`rpe ()`
obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  filter(grepl("rpe", mMCT)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = mMCT), pointsize = 0.8, alpha = 0.5) +
  scattermore::geom_scattermore(data = . %>% filter(leiden3 %in% sus_nonneural$mueller),
                                color = 'red', pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() 

mueller markers

https://pmc.ncbi.nlm.nih.gov/articles/PMC2665263/

Most mueller clusters express known markers - two have low expression of most and are likely to be removed.

44,71 are likely unique on the UMAP view because of AKAP4 and MAP6D1 (markers in diff testing…not certain what significance these have)

tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("rpe|mueller|melano",mMCT)) %>% 
  left_join(conv_table) %>% 
  filter(SYMBOL %in% c("RLBP1","SLC1A3","SOX2","CRABP1","DKK3", "GPR37", 'GAG12B' ,'MAP6D1','AKAP4')) %>% 
  mutate(base = as.factor(base)) %>% 
  mutate(base = case_when(grepl("mueller", mMCT) ~ paste0(base, ' - ', mMCT),
                          TRUE ~ base)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange')


sus_nonneural$mueller <- c(66,95)
obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  filter(grepl("mueller", mMCT)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = mMCT), pointsize = 0.8, alpha = 0.5) +
  scattermore::geom_scattermore(data = . %>% filter(leiden3 %in% sus_nonneural$mueller),
                                color = 'red', pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() 

astrocyte markers

As astrocytes are very similar to Mueller (and have some overlapping marker genes) I am labelling both types of clusters. GFAP is the one “canonical” astrocyte marker. Also tossing in the canonical RLBP1 (mueller) marker to check for mixed clusters.

Oddly enough half of the astrocyte clusters are low GFAP….which is considered a canonical marker. This review (https://www.researchgate.net/publication/354612808_Beyond_the_GFAP-Astrocyte_Protein_Markers_in_the_Brain/link/615c3a4cc04f5909fd7dd9ae/download?_tp=eyJjb250ZXh0Ijp7ImZpcnN0UGFnZSI6InB1YmxpY2F0aW9uIiwicGFnZSI6InB1YmxpY2F0aW9uIn19) says FMN2 and NEBL are also high in brain astrocytes and indeed these low GFAP astrocyte are high in these. So I’m also propossing a “sub label” classification of high GFAP and high FMN2 astrocytes.

relabelling 67 are an astrocyte also.

tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("rpe|mueller|melano",mMCT)) %>% 
  left_join(conv_table) %>% 
  filter(SYMBOL %in% c("GFAP","RLBP1",'FMN2',"NEBL")) %>% 
  mutate(base = as.factor(base)) %>% 
  mutate(base = case_when(grepl("astro|mueller", mMCT) ~ paste0(base, ' - ', mMCT),
                          TRUE ~ base)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-5, 0,5), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange')


sus_nonneural$astrocyte <- c(42,2,56,52)

relabel_nonneural$astrocyte <- c(67)


hr_nonneural$`astrocyte (fmn2+)` <- c(2,42,52,56)
hr_nonneural$`astrocyte (gfap+)` <- c(37,28,55,67)
obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  filter(grepl("astro", mCT)) %>% 
  left_join(hr_nonneural %>%enframe(name = 'CT', value = 'leiden3') %>% unnest(leiden3)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = CT), pointsize = 0.8, alpha = 1) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() 
Joining with `by = join_by(leiden3)`

fibroblasts (and beam and jct)

Read over the van Zyl, Sanes outlow tract paper (https://www.pnas.org/doi/full/10.1073/pnas.2001250117) and….based on what I see here I don’t think the beam cells are a unique cell type…they just look like fibroblasts. So I’m going to drop the beam label and call them fibroblasts. JCT is ANGPTL7 - relabelling cluster 65 to JCT as it doesn’t have any strong fibroblast marker signature and adding in 108 as they are next to each other in the hclust.

a <- obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  filter( MCT_scANVI_step4 == 'fibroblast') %>% 
  mutate(tissue = case_when(grepl("RPE|Cho", tissue) ~ "RPE-Choroid", 
                            grepl("Cornea", tissue) ~ "Cornea", 
                            grepl("Iris", tissue) ~ "Iris", 
                            TRUE ~ tissue)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = tissue), pointsize = 0.8, alpha = 1) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot()
b <- obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  filter( MCT_scANVI_step4 == 'fibroblast') %>% 
  mutate(tissue = case_when(grepl("RPE|Cho", tissue) ~ "RPE-Choroid", 
                            grepl("Cornea", tissue) ~ "Cornea", 
                            grepl("Iris", tissue) ~ "Iris", 
                            TRUE ~ tissue)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = a_Ratio_sum), pointsize = 0.8, alpha = 1) +
  scale_color_viridis_c() +
  cowplot::theme_cowplot()
c <- obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  filter( MajorCellType == 'fibroblast') %>% 
  mutate(tissue = case_when(grepl("RPE|Cho", tissue) ~ "RPE-Choroid", 
                            grepl("Cornea", tissue) ~ "Cornea", 
                            grepl("Iris", tissue) ~ "Iris", 
                            TRUE ~ tissue)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = tissue), pointsize = 0.8, alpha = 1) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot()
cowplot::plot_grid(a,b, c, nrow =1)

tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("rpe|mueller|melano",mMCT)) %>% 
  left_join(conv_table) %>% 
  #filter(SYMBOL %in% c("MGP","MYOC","MEG3","DCN","APOD","ANGPTL7","EFEMP1","BMP5","PRRX1")) %>% 
  filter(SYMBOL %in% c("LUM","DCN","VIM","PDGFRA","COL1A2", # https://www.nature.com/articles/s42003-020-0922-4
                       "MGP","MYOC","MEG3","DCN","APOD","ANGPTL7","EFEMP1","BMP5","PRRX1")) %>% 
  mutate(base = as.factor(base)) %>% 
  mutate(base = case_when(grepl("fibro|beam|jct|schwa", mMCT) ~ paste0(base, ' - ', mMCT),
                          TRUE ~ base)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange',column_names_max_height=unit(12, "cm"))



sus_nonneural$fibroblast <- c(14,121)

#relabel_nonneural <- list()

relabel_nonneural$jct <- c(65,108)

obs_nonneural$labels %>% filter(leiden3 %in% c(sus_nonneural$fibroblast, relabel_nonneural$jct)) %>% data.frame %>% select(leiden3, mMCT, TotalCount, subCT, subCount) 
p <- ggtree(hclust_sim)
p$data <- p$data %>% 
  left_join(obs_nonneural$labels %>% 
              mutate(note = case_when(leiden3 %in% sus_nonneural$fibroblast  ~ 'sus_nonneural')), by = c("label" = "leiden3")) 

p + layout_dendrogram() +
  geom_tiplab(aes(label = paste(label, mMCT, sep = ' - '), color = mCT)) + 
  theme_dendrogram(plot.margin=margin(16,16,300,16)) +
  geom_tippoint(aes(shape = note), size= 6) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  guides(color="none")

obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  left_join(relabel_nonneural %>% enframe(name = 'newCT', value = 'leiden3') %>% unnest(leiden3), 
            by = 'leiden3') %>% 
  mutate(mCT = case_when(!is.na(newCT) ~ newCT,
                         TRUE ~ mCT)) %>% 
  filter(grepl("beam|fibro|jct", mMCT)) %>% 
  mutate(mCT = case_when(mCT == 'beam' ~ 'fibroblast',
                         TRUE ~ mCT)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = mCT), pointsize = 0.8, alpha = 1) +
  scattermore::geom_scattermore(data = . %>% filter(leiden3 %in% sus_nonneural$fibroblast),
                                color = 'red', pointsize = 0.8, alpha = 1) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3), max.overlaps = Inf) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() 

schwann

Drop three schwann clusters that either lack markers or appear to be mixed with other cell type signatures



tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("schwa",mMCT)) %>% 
  left_join(conv_table %>% select(SYMBOL,ENSEMBL) %>% unique()) %>% 
  filter(SYMBOL %in% c("CD9","PLP1","LGI4")) %>% 
  mutate(base = as.factor(base)) %>% 
  mutate(base = case_when(grepl("schwa", mMCT) ~ paste0(base, ' - ', mMCT),
                          TRUE ~ base)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange')


sus_nonneural$schwann <- c(102,125,116)

obs_nonneural$labels %>% filter(leiden3 %in% c(sus_nonneural$schwann )) %>% data.frame %>% select(leiden3, mMCT, TotalCount, subCT, subCount) 
obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  filter(grepl("schwa", mCT)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = mCT), pointsize = 0.8, alpha = 1) +
  scattermore::geom_scattermore(data = . %>% filter(leiden3 %in% sus_nonneural$schwann  ),
                                color = 'red', pointsize = 0.8, alpha = 1) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3), max.overlaps = Inf) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() 

epithelial / endothelial



tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("schwa",mMCT)) %>% 
  left_join(conv_table %>% select(SYMBOL,ENSEMBL) %>% unique()) %>% 
  filter(SYMBOL %in% c("ALPL","VWF","CD59", #endo
                       "POU6F2", "NALF1", "PECAM1", "CLDN1", #endo
                       "KRT24","MOXD1", "PAX6", "KLF5", "MET", "KIT",# epithelial
                       "ANXA1", "TGFBI","KRT12","KRT14", "KRT3", "KRT5", "COL17A1", # "corneal epithelial"
                       "ESAM","BCAM","GJA4")) %>%  # endo
  mutate(base = as.factor(base)) %>% 
  mutate(base = case_when(grepl("epi|endo", mMCT) ~ paste0(base, ' - ', mMCT),
                          TRUE ~ base)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange', 
                        column_names_max_height = unit(12,'cm'))


#sus_nonneural$epithelial <- c(89)
#sus_nonneural$endothelial <- c(64)
relabel_nonneural$endothelial <- c(29,14)

obs_nonneural$labels %>% filter(leiden3 %in% c(sus_nonneural$epithelial, sus_nonneural$endothelial, relabel_nonneural$endothelial, relabel_nonneural$epithelial)) %>% data.frame %>% select(leiden3, mMCT, TotalCount, subCT, subCount) %>% DT::datatable()

epithelial higher resolution

https://www.ncbi.nlm.nih.gov/pmc/articles/PMC8733776/

Type I keratins (K9-K21, K23, Ha1-8) are smaller and acidic compared to the larger, neutral-basic type II keratins (K1-K8, Hb1-6).
tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("schwa",mMCT)) %>% 
  left_join(conv_table %>% select(SYMBOL,ENSEMBL) %>% unique()) %>% 
  #filter(grepl("^KRT\\d", SYMBOL)) %>% 
  filter(SYMBOL %in% (hvg %>% left_join(conv_table, by = c('V2' = 'ENSEMBL')) %>% filter(grepl("^KRT|^LAMC|^DES|^NEF",SYMBOL)) %>% pull(SYMBOL))) %>% 
  filter(grepl("epith", mCT) | base %in% relabel_nonneural$epithelial) %>% 
  mutate(base = as.factor(base)) %>% 
  mutate(base = paste0(base, ' - ', mCT)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange', 
                        column_names_max_height = unit(12,'cm'))


hr_nonneural$`epithelial (non keratinized)` <- c(6,25,89)
hr_nonneural$`epithelial (type ii)` <- c(5,17,70)
hr_nonneural$`epithelial (type i)` <- obs_nonneural$labels %>% 
  filter(mCT == 'epithelial', 
         !leiden3 %in% 
           (hr_nonneural %>% enframe() %>% unnest(value) %>% pull(value))) %>% 
  pull(leiden3)

endothelial higher resolution

#Pecam1 (Cd31), Cdh5 and Flt1 

tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  left_join(conv_table %>% select(SYMBOL,ENSEMBL) %>% unique()) %>% 
  filter(SYMBOL %in% c("PECAM1","FLT1","VWF","MICAL2","AKT3","ADGRL4","ZEB1","CCNY","PIK3C2A")) %>% 
  filter(grepl("endoth", mCT) | base %in% relabel_nonneural$endothelial) %>% 
  mutate(base = as.factor(base)) %>% 
  mutate(base = paste0(base, ' - ', mCT)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange', 
                        column_names_max_height = unit(12,'cm'))


hr_nonneural$`endothelial (flt1+)` <- c(14,21,29,76,81)
hr_nonneural$`endothelial (flt1-)` <- c(31,64)
obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  left_join(hr_nonneural %>% enframe(name = 'newCT', value = 'leiden3') %>% unnest(leiden3), 
            by = 'leiden3') %>% 
  mutate(mCT = case_when(!is.na(newCT) ~ newCT,
                         TRUE ~ mCT)) %>% 
  filter(grepl("epi|endo", mCT)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = mCT), pointsize = 0.8, alpha = 1) +
  scattermore::geom_scattermore(data = . %>% filter(leiden3 %in% c(sus_nonneural$endothelial, sus_nonneural$epithelial  )),
                                color = 'red', pointsize = 0.8, alpha = 1) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3, mCT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3), max.overlaps = Inf) +
  scale_color_manual(values = c(pals::alphabet(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() 

melanocytes

88 dropped for pigmentation markers (DCT, MLANA, TYR)

Two melanocyte clusters are confident by the machine learning / author labels but lack pigmentation (probably?) as they have low DCT/MLANA/TYR expression. Labelled as unpigmented.



tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("schwa",mMCT)) %>% 
  left_join(conv_table %>% select(SYMBOL,ENSEMBL) %>% unique()) %>% 
  filter(SYMBOL %in% c("MLANA","DCT","AQP1", "PAX3","MET", "KIT","LEF1","TYR","TYRP1")) %>%  # endo
  mutate(base = as.factor(base)) %>% 
  mutate(base = case_when(grepl("melan", mMCT) ~ paste0(base, ' - ', mMCT),
                          TRUE ~ base)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange',
                        column_names_max_height = unit(12,'cm'))


sus_nonneural$melanocyte <- c(88)
hr_nonneural$`melanocyte (unpigmented)` <- c(97,96)
hr_nonneural$`melanocyte (pigmented)` <- c(26,115,109,41,11,47)

obs_nonneural$labels %>% filter(leiden3 %in% c(sus_nonneural$melanocyte)) %>% data.frame %>% select(leiden3, mMCT, TotalCount, subCT, subCount) %>% DT::datatable()
obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  left_join(hr_nonneural %>% enframe(name = 'newCT', value = 'leiden3') %>% unnest(leiden3), 
            by = 'leiden3') %>% 
  mutate(mCT = case_when(!is.na(newCT) ~ newCT,
                         TRUE ~ mCT)) %>% 
  filter(grepl("melano", mCT)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = mCT), pointsize = 0.8, alpha = 1) +
  scattermore::geom_scattermore(data = . %>% filter(leiden3 %in% c(sus_nonneural$melanocyte )),
                                color = 'red', pointsize = 0.8, alpha = 1) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3, mCT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3), max.overlaps = Inf) +
  scale_color_manual(values = c(pals::alphabet(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() 

immune

https://www.pnas.org/doi/full/10.1073/pnas.2001250117: 
Our dataset also included four types of immune cells: B cells, Natural Killer (NK)/T cells, mast cells, and macrophages. The macrophages (C4) were CD163+ and LYVE1+ (49) and localized predominantly to the TM (Fig. 4G). They also expressed CD68, CD14, CCL3, CCL4, CXCL8, IL1B, TREM2, and MS4A genes, all of which have been associated with macrophages in other tissues. Mast cells (C13) were localized to the TM using the marker IL1RL1 and also expressed CPA3, RGS13, and KIT (SI Appendix, Fig. S2F). B cells (C14), characterized by expression of CD27, CD79A, IGHM, IGKC, MZB1, and JCHAIN, were found in only one donor sample but were identified histologically in tissues from other donors using the marker CD27 (SI Appendix, Fig. S2G). NK/T cells (c10) were identified by differential expression of the genes CD2, CD3D, IL7R, TRAC, GZMA, GZMB, and NKG7.
immune_clusters <- c(78,117,16,110,75,35,50,92,86,30,59)

tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("schwa",mMCT)) %>% 
  left_join(conv_table %>% select(SYMBOL,ENSEMBL) %>% unique()) %>% 
  filter(SYMBOL %in% c("LYVE1","CD163",
                       "C1QA","CTSS","B2M","HLA-DPA1","HLA-DPB1", "HLA-DRA",
                       "CD27","CD79A",
                       "CD2",
                       "IL1RL1",
                       "HBB","HBA")) %>% #"C1QA","CTSS","B2M","HLA-DPA1","HLA-DPB1", "HLA-DRA",
  # "P2RY12","TMEM119","SIGLECH","SERINC3",
  # "LPL","CST7","SPP1","CSTB",
  # "CCR1","CCR2")) %>% 
  
  #c("ARG1","CD86","TNF","NOS2","RETNLA","MGL2","CHIL3", #macrophage
  #  "TMEM119","P2Y12R","OLFML3","SALL1", "LYVE1","CD163",
  #  "HBB")) %>%  #microglia
  #"CD68","CD14", "GAPDH","PGAM1")) %>% #, #macrophage)) %>% 
  mutate(base = as.factor(base)) %>% 
  mutate(base = case_when(grepl("microg|mono|macro|immune|red", mMCT) ~ paste0(base, ' - ', mMCT),
                          base %in% immune_clusters ~ paste0(base, ' - ', mMCT),
                          TRUE ~ base)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange', 
                        column_names_max_height = unit(12,'cm'))



relabel_nonneural$microglia <- c(30,59,86, 92)
relabel_nonneural$`t/nk` <- c(16,112)
relabel_nonneural$`b` <- c(78)
relabel_nonneural$macrophage <- c(35,50,75)
relabel_nonneural$lymophocyte <- c(110)
relabel_nonneural$mast <- c(117)
sus_nonneural$`red blood` <- c(122)
obs_nonneural$labels %>% filter(leiden3 %in% c(immune_clusters)) %>% data.frame %>% select(leiden3, mMCT, TotalCount, subCT, subCount) %>% DT::datatable()

p <- ggtree(hclust_sim)
p$data <- p$data %>% left_join(obs_nonneural$labels %>% 
                                 mutate(note = case_when(leiden3 %in% c(immune_clusters,104,123) ~ 'immune')), by = c("label" = "leiden3")) 

p + layout_dendrogram() +
  geom_tiplab(aes(label = paste(label, mMCT, note, sep = ' - '), color = mCT)) + 
  geom_tippoint(aes(shape = note), size= 3) +
  theme_dendrogram(plot.margin=margin(16,16,300,16)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  guides(color="none")

obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  left_join(relabel_nonneural %>% enframe(name = 'newCT', value = 'leiden3') %>% unnest(leiden3), 
            by = 'leiden3') %>% 
  mutate(mCT = case_when(!is.na(newCT) ~ newCT,
                         TRUE ~ mCT)) %>% 
  filter(leiden3 %in% immune_clusters) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = mCT), pointsize = 0.8, alpha = 1) +
  scattermore::geom_scattermore(data = . %>% filter(leiden3 %in% c(sus_nonneural$melanocyte )),
                                color = 'red', pointsize = 0.8, alpha = 1) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3, mCT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3), max.overlaps = Inf) +
  scale_color_manual(values = c(pals::glasbey(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() 

ppe / ape / pce / npce /ciliary body

all epithelium

ppe = posteriar pigmented epi ape = anterior

pce = pigmented ciliary npce = nonpigmented

The ciliary body cells from SRP255012 are seemingly a combo of pigmented and unpigmented epithelium (clusters 120, 90). The PCE NPCE are in distinct clusters from SRP364915. Though some of them fail to show the markers that van Zyl et al use, so perhaps these are lower quality cells? Marking those for removal. Same for PPE / APE.

tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("schwa",mMCT)) %>% 
  left_join(conv_table %>% select(SYMBOL,ENSEMBL) %>% unique()) %>% 
  filter(SYMBOL %in% c("ATP6V1C2","CCBE1","LRP2","COL9A1", "HTR2C","MECOM",
                       "IGFN1","SLC7A2", "GALNT14",
                       "DCT", "TYR","MLANA")) %>% 
  filter(grepl("ppe|ape|ciliary|pce", mMCT)) %>% 
  #"CD68","CD14", "GAPDH","PGAM1")) %>% #, #macrophage)) %>% 
  mutate(base = as.factor(base)) %>% 
  mutate(base = case_when(grepl("ppe|ape|ciliary|pce", mMCT) ~ paste0(base, ' - ', mMCT),
                          TRUE ~ base)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange', 
                        column_names_max_height = unit(12,'cm'))



obs_nonneural$labels %>% filter(grepl("ppe|ape|ciliary|pce", mMCT)) %>% data.frame %>% select(leiden3, mMCT, TotalCount, subCT, subCT_Ratio, subCount) %>% DT::datatable()

p <- ggtree(hclust_sim)
p$data <- p$data %>% left_join(obs_nonneural$labels %>% 
                                 mutate(note = case_when(grepl("ppe|ape|ciliary|pce|muscle", mMCT) ~ 'e')), by = c("label" = "leiden3")) 

p + layout_dendrogram() +
  geom_tiplab(aes(label = paste(label, mMCT, note, sep = ' - '), color = mCT)) + 
  geom_tippoint(aes(shape = note), size= 3) +
  theme_dendrogram(plot.margin=margin(16,16,300,16)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  guides(color="none")


sus_nonneural$pce <- c(24)
sus_nonneural$npce <- c(82,68, 67)
sus_nonneural$ape <- c(20)
sus_nonneural$ppe <- c(19,61)
obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  left_join(relabel_nonneural %>% enframe(name = 'newCT', value = 'leiden3') %>% unnest(leiden3), 
            by = 'leiden3') %>% 
  mutate(mCT = case_when(!is.na(newCT) ~ newCT,
                         TRUE ~ mCT)) %>% 
  filter(grepl("ppe|ape|pce|ciliary",mMCT)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = mCT), pointsize = 0.8, alpha = 1) +
  scattermore::geom_scattermore(data = . %>% filter(leiden3 %in% c(sus_nonneural$ppe, sus_nonneural$pce, sus_nonneural$npce, sus_nonneural$ape )),
                                color = 'red', pointsize = 0.8, alpha = 1) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3, mCT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3), max.overlaps = Inf) +
  scale_color_manual(values = c(pals::alphabet2()[2:10], pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() 

pericyte

tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("schwa",mMCT)) %>% 
  left_join(conv_table %>% select(SYMBOL,ENSEMBL) %>% unique()) %>% 
  filter(SYMBOL %in% c("NOTCH3","PDGFRB","MCAM","HIGD1B", "ADCY3")) %>% 
  #"CD68","CD14", "GAPDH","PGAM1")) %>% #, #macrophage)) %>% 
  mutate(base = as.factor(base)) %>% 
  mutate(base = case_when(grepl("pericyte", mMCT) ~ paste0(base, ' - ', mMCT),
                          TRUE ~ base)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange', 
                        column_names_max_height = unit(12,'cm'))



obs_nonneural$labels %>% filter(grepl("pericyte", mMCT)) %>% data.frame %>% select(leiden3, mMCT, TotalCount, subCT, subCT_Ratio, subCount) %>% DT::datatable()

p <- ggtree(hclust_sim)
p$data <- p$data %>% left_join(obs_nonneural$labels %>% 
                                 mutate(note = case_when(grepl("pericyte", mMCT) ~ 'e')), by = c("label" = "leiden3")) 

p + layout_dendrogram() +
  geom_tiplab(aes(label = paste(label, mMCT, note, sep = ' - '), color = mCT)) + 
  geom_tippoint(aes(shape = note), size= 3) +
  theme_dendrogram(plot.margin=margin(16,16,300,16)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  guides(color="none")


relabel_nonneural$pericyte <- c(81,32,122)
obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  left_join(relabel_nonneural %>% enframe(name = 'newCT', value = 'leiden3') %>% unnest(leiden3), 
            by = 'leiden3') %>% 
  mutate(mCT = case_when(!is.na(newCT) ~ newCT,
                         TRUE ~ mCT)) %>% 
  filter(leiden3 %in% relabel_nonneural$pericyte) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = mCT), pointsize = 0.8, alpha = 1) +
  scattermore::geom_scattermore(data = . %>% filter(leiden3 %in% c(sus_nonneural$ppe, sus_nonneural$pce, sus_nonneural$npce, sus_nonneural$ape )),
                                color = 'red', pointsize = 0.8, alpha = 1) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3, mCT, tissues) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = tissues), max.overlaps = Inf) +
  scale_color_manual(values = c(pals::alphabet2()[2:10], pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() 

lens

Cluster 7 is comprised of most of the lens cells (https://www.pnas.org/doi/full/10.1073/pnas.2200914119, fig 4)

tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("schwa",mMCT)) %>% 
  left_join(conv_table %>% select(SYMBOL,ENSEMBL) %>% unique()) %>% 
  filter(SYMBOL %in% c("KIF26B","BNIP3")) %>% 
  #"CD68","CD14", "GAPDH","PGAM1")) %>% #, #macrophage)) %>% 
  mutate(base = as.factor(base)) %>% 
  mutate(base = case_when(grepl("fiber", mMCT) ~ paste0(base, ' - ', mMCT),
                          TRUE ~ base)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange', 
                        column_names_max_height = unit(12,'cm'))



obs_nonneural$labels %>% filter(grepl("fiber", mMCT)) %>% data.frame %>% select(leiden3, mMCT, TotalCount, subCT, subCT_Ratio, subCount) %>% DT::datatable()

p <- ggtree(hclust_sim)
p$data <- p$data %>% left_join(obs_nonneural$labels %>% 
                                 mutate(note = case_when(grepl("fiber", mMCT) ~ 'e')), by = c("label" = "leiden3")) 

p + layout_dendrogram() +
  geom_tiplab(aes(label = paste(label, mMCT, note, sep = ' - '), color = mCT)) + 
  geom_tippoint(aes(shape = note), size= 3) +
  theme_dendrogram(plot.margin=margin(16,16,300,16)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  guides(color="none")


relabel_nonneural$lens <- c(7)
obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  left_join(relabel_nonneural %>% enframe(name = 'newCT', value = 'leiden3') %>% unnest(leiden3), 
            by = 'leiden3') %>% 
  mutate(mCT = case_when(!is.na(newCT) ~ newCT,
                         TRUE ~ mCT)) %>% 
  filter(leiden3 %in% relabel_nonneural$pericyte) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = mCT), pointsize = 0.8, alpha = 1) +
  scattermore::geom_scattermore(data = . %>% filter(leiden3 %in% c(sus_nonneural$ppe, sus_nonneural$pce, sus_nonneural$npce, sus_nonneural$ape )),
                                color = 'red', pointsize = 0.8, alpha = 1) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3, mCT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3), max.overlaps = Inf) +
  scale_color_manual(values = c(pals::alphabet2()[2:10], pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() 

(smooth) muscle, sphincter (iris muscle)

All are fine as is (mCT)

tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("schwa",mMCT)) %>% 
  left_join(conv_table %>% select(SYMBOL,ENSEMBL) %>% unique()) %>% 
  #filter(SYMBOL %in% c("CHRM3","DES","MYH11", 'PPP1R12A','ACTA2','CNN1','TAGLN')) %>% 
  filter(SYMBOL %in% c("PPP1R12A",    "ATP2A2",      "CHRM3",       "PDE3A", "ACTA2", "DES", "ADRA1A", "TPM2", "MYOC",
                       "GLIS1", "CHRM3","TPM1",'COL4A2', 'IRAG1',"MYH11", "IRAG1","ST6GALNAC5")) %>%  # pericyte
 # filter(SYMBOL %in% y) %>% 
  mutate(base = as.factor(base)) %>% 
  mutate(base = case_when(grepl("muscle|sphinc", mMCT) ~ paste0(base, ' - ', mMCT),
                          TRUE ~ base)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df =
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange', row_names_side = 'left',
                        column_names_max_height = unit(12,'cm'))



obs_nonneural$labels %>% filter(grepl("muscle", mMCT)) %>% data.frame %>% select(leiden3, mMCT, TotalCount, subCT, subCT_Ratio, subCount) %>% DT::datatable()

p <- ggtree(hclust_sim)
p$data <- p$data %>% left_join(obs_nonneural$labels %>%
                                 mutate(note = case_when(grepl("muscle", mMCT) ~ 'muscle')), by = c("label" = "leiden3"))

p + layout_dendrogram() +
  geom_tiplab(aes(label = paste(label, mMCT, note, sep = ' - '), color = mCT)) +
  geom_tippoint(aes(shape = note), size= 3) +
  theme_dendrogram(plot.margin=margin(16,16,300,16)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) +
  guides(color="none")


# simplify muscle labelling
relabel_nonneural$muscle <- c(10,27,72,74,93,94,99)
hr_nonneural$`muscle (ciliary)` <- c(10,27)
hr_nonneural$`muscle (smooth)` <- c(72,74,99)
hr_nonneural$`muscle (iris dilator)` <- c(93)
hr_nonneural$`muscle (iris sphincter)` <- c(94)
obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  filter(mCT %in% c("muscle","smooth muscle", "sphincter")) %>% 
  left_join(hr_nonneural %>%enframe(name = 'CT', value = 'leiden3') %>% unnest(leiden3)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = CT), pointsize = 0.8, alpha = 1) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3, CT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3), max.overlaps = Inf) +
  scale_color_manual(values = c(pals::alphabet2()[2:10], pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() 
Joining with `by = join_by(leiden3)`

opc / oligo

There a PCDH9+ oligodendrocyte and PCDH9- populations

tib <- diff_nonneural$diff_testing %>% 
  left_join(obs_nonneural$labels, by = c('base'='leiden3')) %>% 
  #filter(grepl("schwa",mMCT)) %>% 
  left_join(conv_table %>% select(SYMBOL,ENSEMBL) %>% unique()) %>% 
  filter(SYMBOL %in% c("GRIA4","CTNNA3","PCDH9","LRRTM3")) %>% 
  #"CD68","CD14", "GAPDH","PGAM1")) %>% #, #macrophage)) %>% 
  mutate(base = as.factor(base)) %>% 
  mutate(base = case_when(grepl("opc|oligo", mMCT) ~ paste0(base, ' - ', mMCT),
                          TRUE ~ base)) %>% 
  select(SYMBOL, base, logfoldchanges) %>% 
  pivot_wider(values_from = logfoldchanges, names_from = base)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
mat <- tib %>% select(-1) %>% as.matrix()
row.names(mat) <- tib %>% pull(1)

CT = colnames(tib)[-1] %>% 
  gsub('\\d+ - ','', .) 

#names(CT) <- c(pals::alphabet(),pals::glasbey())

ha_column <- ComplexHeatmap::HeatmapAnnotation(
  df = 
    data.frame(CT)
)

col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
ComplexHeatmap::Heatmap(mat, col=col_fun,
                        name = 'logFoldChange', 
                        column_names_max_height = unit(12,'cm'))



obs_nonneural$labels %>% filter(grepl("opc|oligo", mMCT)) %>% data.frame %>% select(leiden3, mMCT, TotalCount, subCT, subCT_Ratio, subCount) %>% DT::datatable()

hr_nonneural$`oligodendrocyte (PCDH9+)` <- c(12,36)
hr_nonneural$`oligodendrocyte (PCDH9-)` <- c(0)
obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  filter(mCT %in% c("opc","oligo")) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = mCT), pointsize = 0.8, alpha = 1) +
  ggrepel::geom_label_repel(data = . %>% group_by(leiden3, mCT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = leiden3), max.overlaps = Inf) +
  scale_color_manual(values = c(pals::alphabet2()[2:10], pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() 

NEW

Update overall graphics on the new labels

UMAP

relabel_nonneural_long <- relabel_nonneural %>% enframe(name = 'newCT', value = 'leiden3') %>% unnest(leiden3) 
hr_long <- hr_nonneural %>% enframe(name = 'hrCT', value = 'leiden3') %>% unnest(leiden3) 
sus_nonneural_long <- sus_nonneural %>% enframe(name = 'newCT', value = 'leiden3') %>% unnest(leiden3) %>% 
  filter(!leiden3 %in% relabel_nonneural_long$leiden3) %>% 
  filter(!leiden3 %in% hr_long$leiden3)

obs_nonneural$nobs <- obs_nonneural$obs %>% 
  left_join(obs_nonneural$labels, by = 'leiden3') %>% 
  left_join(relabel_nonneural_long, 
            by = 'leiden3') %>%
  left_join(hr_long, 
            by = 'leiden3') %>%
  filter(!leiden3 %in% (sus_nonneural_long$leiden3)) %>% 
  mutate(CT = case_when(mCT == 'beam' ~ 'fibroblast',
                        !is.na(newCT) ~ newCT,
                        TRUE ~ mCT),
         hrCT = case_when(!is.na(hrCT) ~ hrCT,
                          TRUE ~ CT))

obs_nonneural$nlabels <- obs_nonneural$labels %>% 
  left_join(relabel_nonneural_long, 
            by = 'leiden3') %>%
  left_join(hr_long, 
            by = 'leiden3') %>%
  filter(!leiden3 %in% (sus_nonneural_long$leiden3)) %>% 
  mutate(CT = case_when(mCT == 'beam' ~ 'fibroblast',
                        !is.na(newCT) ~ newCT,
                        TRUE ~ mCT),
         hrCT = case_when(!is.na(hrCT) ~ hrCT,
                          TRUE ~ CT))
obs_nonneural$nobs %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = CT), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(CT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = CT, color = CT)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none")


obs_nonneural$nobs %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = as.factor(hrCT)), pointsize = 2.1, alpha = 0.5) +
    ggrepel::geom_label_repel(data = . %>% group_by(CT, hrCT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = hrCT, color = hrCT)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey(), pals::alphabet(), pals::brewer.set1(9), pals::kelly()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none") + facet_wrap(~CT)

hclust

pb <- data.table::fread('~/data/scEiaD_modeling/hs111_mature_eye_nonneural/hs111_mature_eye_20241001_full__NONneural2000hvg_200e_30l.pseudoBulk.leiden3.csv.gz')
colnames(pb) <- gsub("\\.\\d+","",colnames(pb))
hvg <- data.table::fread('~/data/scEiaD_modeling/hs111_mature_eye_nonneural/hvg2000.csv.gz')[-1,]
rnames <- pb$V1
clust <- str_extract(rnames, '\\d+') %>% as.integer()
pb <- pb[,-1] %>% as.matrix()
row.names(pb) <- as.character(clust)
pb <- pb[as.character(obs_nonneural$nlabels$leiden3),]

pb_norm <- metamoRph::normalize_data(t(pb), sample_scale = 'cpm') %>% t()
Sample CPM scaling
log1p scaling
pb_norm <- pb_norm[,hvg$V2]
#pb_norm <- pb_norm[,hvg$V2[!hvg$V2 %in% c(cc_genes,ribo_genes)]]
# https://stats.stackexchange.com/questions/31565/compute-a-cosine-dissimilarity-matrix-in-r
sim <- pb_norm / sqrt(rowSums(pb_norm * pb_norm))
sim <- sim %*% t(sim)
D_sim <- as.dist(1 - sim)
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
hclust_sim <- hclust(D_sim, method = 'average')

hclust_sim$labels <- obs_nonneural$nlabels %>% pull(leiden3)

library(ggtree)
p <- ggtree(hclust_sim)
p$data <- p$data %>% left_join(obs_nonneural$nlabels, by = c("label" = "leiden3")) %>%
  mutate(techRatio = round(techRatio, digits = 2))
p + layout_dendrogram() +
  geom_tiplab(aes(label = paste(label, CT, studyCount, TotalCount, techRatio, sep = ' - '), color = CT)) +
  theme_dendrogram(plot.margin=margin(16,16,300,16)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) +
  guides(color="none")



p <- ggtree(hclust_sim)
p$data <- p$data %>% left_join(obs_nonneural$nlabels %>% mutate(studies = case_when(studyCount ==1 ~ studies,
                                                                                    TRUE ~ "multiple")), by = c("label" = "leiden3"))

p + layout_dendrogram() +
  geom_tiplab(aes(label = paste(label, CT, studies, sep = ' - '), color = CT)) +
  geom_tippoint(aes(shape = studies), size= 3) +
  theme_dendrogram(plot.margin=margin(16,16,300,16)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) +
  guides(color="none")

NA
NA
NA
NA

Outputs

save(obs_nonneural, file = 'Human_Mature_Eye_full__stage4_NONneural.obs.freeze20241107.Rdata')
obs_nonneural$nobs %>% select(barcode, leiden3, CT, hrCT) %>% write_csv('Human_Mature_Eye_full__stage4_NONneural.CTcalls.freeze20241107.csv.gz')
LS0tCnRpdGxlOiAiSHVtYW4gTWF0dXJlIEV5ZSwgTk9OLU5ldXJhbCBBc3Nlc3NtZW50IgpvdXRwdXQ6CiBodG1sX25vdGVib29rOgogIGF1dGhvcjogIkRhdmlkIE1jR2F1Z2hleSIKICBkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCiAgdGhlbWU6IGZsYXRseQogIHRvYzogdHJ1ZQogIHRvY19mbG9hdDogdHJ1ZQogIGNvZGVfZm9sZGluZzogc2hvdwotLS0KCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBtZXNzYWdlID0gRkFMU0UsICB3YXJuaW5nID0gRkFMU0UsCiAgY29sbGFwc2UgPSBUUlVFLAogIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOCwKICBjb21tZW50ID0gIiM+IiwKICBkcGk9MzAwCikKYGBgCgojIFN0YWdlIDQKCiMjIEJpb3d1bGYyCmBgYHtiYXNoLCBldmFsID0gRkFMU0V9CmNkIC9kYXRhL09HVkZCX0JHL3NjRWlhRC8yMDI0XzAyXzI4L3NuYWtlb3V0L2hzMTExX21hdHVyZV9leWVfZnVsbC9OT05uZXVyYWxfY2VsbHMKbWFtYmEgZGVhY3RpdmF0ZTsgbWFtYmEgYWN0aXZhdGU7IGJhc2ggfi9naXQvc2NFaWFEX21vZGVsaW5nL1NuYWtlbWFrZS53cmFwcGVyLnNoIH4vZ2l0L3NjRWlhRF9tb2RlbGluZy93b3JrZmxvdy9TbmFrZWZpbGUgfi9naXQvc2NFaWFEX21vZGVsaW5nL2NvbmZpZy9jb25maWdfaHMxMTFfbWF0dXJlX2V5ZV9mdWxsX19OT05uZXVyYWwueWFtbCB+L2dpdC9zY0VpYURfbW9kZWxpbmcvY29uZmlnL2NsdXN0ZXIuanNvbgpgYGAKCiMjIEFzc2VzcyBPdXRwdXQKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKc291cmNlKCdhbmFseXNpc19zY3JpcHRzLlInKQpvYnNfbm9ubmV1cmFsIDwtIHB1bGxfb2JzKCd+L2RhdGEvc2NFaWFEX21vZGVsaW5nL2hzMTExX21hdHVyZV9leWVfbm9ubmV1cmFsL2hzMTExX21hdHVyZV9leWVfMjAyNDEwMDFfZnVsbF9fTk9ObmV1cmFsMjAwMGh2Z18yMDBlXzMwbC5vYnMuY3N2Lmd6JywgbWFjaGluZV9sYWJlbCA9ICdNQ1Rfc2NBTlZJX3N0ZXA0JywgZHJvcF9jb2wgPSBGQUxTRSkKZGlmZl9ub25uZXVyYWwgPC0gcHVsbF9kaWZmKCJ+L2RhdGEvc2NFaWFEX21vZGVsaW5nL2hzMTExX21hdHVyZV9leWVfbm9ubmV1cmFsL2hzMTExX21hdHVyZV9leWVfMjAyNDEwMDFfZnVsbF9fTk9ObmV1cmFsMjAwMGh2Z18yMDBlXzMwbC5kaWZmdGVzdGluZy5sZWlkZW4zLmNzdi5neiIpCmBgYAoKIyMjIFJhdGlvIChwZXJjZW50YWdlKSBvZiBsYWJlbGxlZCBjZWxsIHR5cGVzIGZvciBlYWNoIGxlaWRlbjMgY2x1c3RlcgoKYGBge3J9Cm9ic19ub25uZXVyYWwkbGFiZWxzICU+JSAKICBhcnJhbmdlKG1DVCkgJT4lIAogIG11dGF0ZShsZWlkZW4zID0gYXMuZmFjdG9yKGxlaWRlbjMpKSAlPiUgCiAgRFQ6OmRhdGF0YWJsZShmaWx0ZXIgPSAndG9wJykKYGBgCgojIyMgTWl4ZWQgY2x1c3RlcnMKCmBgYHtyfQpvYnNfbm9ubmV1cmFsJGxhYmVscyAlPiUgCiAgZmlsdGVyKGdyZXBsKCIsIiwgbU1DVCkpICU+JSAKICBhcnJhbmdlKG1DVCkgJT4lIAogIG11dGF0ZShsZWlkZW4zID0gYXMuZmFjdG9yKGxlaWRlbjMpKSAlPiUgCiAgRFQ6OmRhdGF0YWJsZShmaWx0ZXIgPSAndG9wJykKYGBgCgojIyBVTUFQIFBsb3RzCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQpvYnNfbm9ubmV1cmFsJG9icyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9ICdsZWlkZW4zJykgJT4lIAogIGdncGxvdChhZXMoeD11bWFwMSx5PXVtYXAyKSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGFlcyhjb2xvciA9IE1DVF9zY0FOVklfc3RlcDQpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMC41KSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gLiAlPiUgZ3JvdXBfYnkoTUNUX3NjQU5WSV9zdGVwNCkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UodW1hcDEgPSBtZWRpYW4odW1hcDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW1hcDIgPSBtZWRpYW4odW1hcDIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IE1DVF9zY0FOVklfc3RlcDQsIGNvbG9yID0gTUNUX3NjQU5WSV9zdGVwNCkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpKSAlPiUgdW5uYW1lKCkpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKb2JzX25vbm5ldXJhbCRvYnMgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBnZ3Bsb3QoYWVzKHg9dW1hcDEseT11bWFwMikpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShhZXMoY29sb3IgPSBNQ1Rfc2NBTlZJKSwgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDAuNSkgKwogIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IC4gJT4lIGdyb3VwX2J5KGxlaWRlbjMpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHVtYXAxID0gbWVkaWFuKHVtYXAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBsZWlkZW4zKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjpnbGFzYmV5KCkpICU+JSB1bm5hbWUoKSkgKyAKICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpvYnNfbm9ubmV1cmFsJG9icyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9ICdsZWlkZW4zJykgJT4lIAogIGdncGxvdChhZXMoeD11bWFwMSx5PXVtYXAyKSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGFlcyhjb2xvciA9IGFzLmZhY3RvcihsZWlkZW4zKSksIHBvaW50c2l6ZSA9IDAuOCwgYWxwaGEgPSAwLjUpICsKICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSAuICU+JSBncm91cF9ieShsZWlkZW4zKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bWFwMiA9IG1lZGlhbih1bWFwMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gbGVpZGVuMykpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpLCBwYWxzOjphbHBoYWJldCgpLHBhbHM6OmtlbGx5KCksIHBhbHM6OnRhYmxlYXUyMCgpLCBwYWxzOjp3YXRsaW5ndG9uKCkpICU+JSB1bm5hbWUoKSkgKyAKICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpvYnNfbm9ubmV1cmFsJG9icyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9ICdsZWlkZW4zJykgJT4lIAogIGdncGxvdChhZXMoeD11bWFwMSx5PXVtYXAyKSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGFlcyhjb2xvciA9IG1NQ1QpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMC41KSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gLiAlPiUgZ3JvdXBfYnkobU1DVCkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UodW1hcDEgPSBtZWRpYW4odW1hcDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW1hcDIgPSBtZWRpYW4odW1hcDIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IG1NQ1QsIGNvbG9yID0gbU1DVCkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpKSAlPiUgdW5uYW1lKCkpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKCmBgYAoKIyMgaGNsdXN0CgpUYWtlIHBzZXVkb2J1bGsgdmFsdWVzIChhdCB0aGUgY2x1c3RlciBsZXZlbCkgYW5kIGhpZXJhcmNoaWNhbGx5IGNsdXN0ZXIgdGhlbSB0byBlbnN1cmUgdGhlcmUgYXJlbid0IGFueSBpc3N1ZXMgaW4gZWl0aGVyIHRoZSBvdmVyYWxsIHN0cnVjdHVyZSAoZS5nLiByb2QgYW5kIGNvbmVzIGFyZSBpbnRlcnNwZXJzZSlkIGFuZC9vciB0byBpZGVudGlmeSBhbnkgcG90ZW50aWFsIG1pc2xhYmVsZWQgY2x1c3RlcnMKCmBgYHtyLCBmaWcud2lkdGggPSAxOCwgZmlnLmhlaWdodCA9IDEwfQpwYiA8LSBkYXRhLnRhYmxlOjpmcmVhZCgnfi9kYXRhL3NjRWlhRF9tb2RlbGluZy9oczExMV9tYXR1cmVfZXllX25vbm5ldXJhbC9oczExMV9tYXR1cmVfZXllXzIwMjQxMDAxX2Z1bGxfX05PTm5ldXJhbDIwMDBodmdfMjAwZV8zMGwucHNldWRvQnVsay5sZWlkZW4zLmNzdi5neicpCmNvbG5hbWVzKHBiKSA8LSBnc3ViKCJcXC5cXGQrIiwiIixjb2xuYW1lcyhwYikpCmh2ZyA8LSBkYXRhLnRhYmxlOjpmcmVhZCgnfi9kYXRhL3NjRWlhRF9tb2RlbGluZy9oczExMV9tYXR1cmVfZXllX25vbm5ldXJhbC9odmcyMDAwLmNzdi5neicpWy0xLF0Kcm5hbWVzIDwtIHBiJFYxCmNsdXN0IDwtIHN0cl9leHRyYWN0KHJuYW1lcywgJ1xcZCsnKSAlPiUgYXMuaW50ZWdlcigpCnBiIDwtIHBiWywtMV0gJT4lIGFzLm1hdHJpeCgpCnJvdy5uYW1lcyhwYikgPC0gYXMuY2hhcmFjdGVyKGNsdXN0KQpwYiA8LSBwYlthcy5jaGFyYWN0ZXIob2JzX25vbm5ldXJhbCRsYWJlbHMkbGVpZGVuMyksXQoKcGJfbm9ybSA8LSBtZXRhbW9ScGg6Om5vcm1hbGl6ZV9kYXRhKHQocGIpLCBzYW1wbGVfc2NhbGUgPSAnY3BtJykgJT4lIHQoKSAKCiMgcmVtb3ZlIGNlbGwgY3ljbGUgZ2VuZXMKY29udl90YWJsZSA8LSBBbm5vdGF0aW9uRGJpOjpzZWxlY3Qob3JnLkhzLmVnLmRiOjpvcmcuSHMuZWcuZGIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXlzPWdzdWIoJ1xcLlxcZCsnLCcnLHVuaXF1ZShjb2xuYW1lcyhwYl9ub3JtKSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5zPWMoIkVOU0VNQkwiLCJTWU1CT0wiLCAiTUFQIiwiR0VORU5BTUUiLCAiRU5UUkVaSUQiKSwga2V5dHlwZT0iRU5TRU1CTCIpCgpjY19nZW5lcyA8LSBodmcgJT4lIG11dGF0ZShFTlNFTUJMID0gZ3N1YigiXFwuXFxkKyIsIiIsVjIpKSAlPiUgCiAgbGVmdF9qb2luKGNvbnZfdGFibGUsIGJ5ID0gIkVOU0VNQkwiKSAlPiUgCiAgbXV0YXRlKGNjX2dlbmVzID0gY2FzZV93aGVuKFNZTUJPTCAlaW4lIChTZXVyYXQ6OmNjLmdlbmVzLnVwZGF0ZWQuMjAxOSAlPiUgdW5saXN0KCkpIH4gVFJVRSkpICU+JSAKICBmaWx0ZXIoY2NfZ2VuZXMpICU+JSBwdWxsKFYyKQpyaWJvX2dlbmVzIDwtIGh2ZyAlPiUgbXV0YXRlKEVOU0VNQkwgPSBnc3ViKCJcXC5cXGQrIiwiIixWMikpICU+JSAKICBsZWZ0X2pvaW4oY29udl90YWJsZSwgYnkgPSAiRU5TRU1CTCIpICU+JSBmaWx0ZXIoZ3JlcGwoIl5SUEx8XlJQU3xeTVQiLFNZTUJPTCkpICU+JSAKICBwdWxsKFNZTUJPTCkKCnBiX25vcm0gPC0gcGJfbm9ybVssaHZnJFYyXQojcGJfbm9ybSA8LSBwYl9ub3JtWyxodmckVjJbIWh2ZyRWMiAlaW4lIGMoY2NfZ2VuZXMscmlib19nZW5lcyldXQojIGh0dHBzOi8vc3RhdHMuc3RhY2tleGNoYW5nZS5jb20vcXVlc3Rpb25zLzMxNTY1L2NvbXB1dGUtYS1jb3NpbmUtZGlzc2ltaWxhcml0eS1tYXRyaXgtaW4tcgpzaW0gPC0gcGJfbm9ybSAvIHNxcnQocm93U3VtcyhwYl9ub3JtICogcGJfbm9ybSkpCnNpbSA8LSBzaW0gJSolIHQoc2ltKQpEX3NpbSA8LSBhcy5kaXN0KDEgLSBzaW0pCgpoY2x1c3Rfc2ltIDwtIGhjbHVzdChEX3NpbSwgbWV0aG9kID0gJ2F2ZXJhZ2UnKQoKaGNsdXN0X3NpbSRsYWJlbHMgPC0gb2JzX25vbm5ldXJhbCRsYWJlbHMgJT4lIHB1bGwobGVpZGVuMykKCmxpYnJhcnkoZ2d0cmVlKQpwIDwtIGdndHJlZShoY2x1c3Rfc2ltKQpwJGRhdGEgPC0gcCRkYXRhICU+JSBsZWZ0X2pvaW4ob2JzX25vbm5ldXJhbCRsYWJlbHMsIGJ5ID0gYygibGFiZWwiID0gImxlaWRlbjMiKSkgJT4lIAogIG11dGF0ZSh0ZWNoUmF0aW8gPSByb3VuZCh0ZWNoUmF0aW8sIGRpZ2l0cyA9ICkpCnAgKyBsYXlvdXRfZGVuZHJvZ3JhbSgpICsKICBnZW9tX3RpcGxhYihhZXMobGFiZWwgPSBwYXN0ZShsYWJlbCwgbU1DVCwgc3R1ZHlDb3VudCwgVG90YWxDb3VudCwgdGVjaFJhdGlvLCBzZXAgPSAnIC0gJyksIGNvbG9yID0gbUNUKSkgKyAKICB0aGVtZV9kZW5kcm9ncmFtKHBsb3QubWFyZ2luPW1hcmdpbigxNiwxNiwzMDAsMTYpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArIAogIGd1aWRlcyhjb2xvcj0ibm9uZSIpCgoKcCA8LSBnZ3RyZWUoaGNsdXN0X3NpbSkKcCRkYXRhIDwtIHAkZGF0YSAlPiUgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzICU+JSBtdXRhdGUoc3R1ZGllcyA9IGNhc2Vfd2hlbihzdHVkeUNvdW50ID09MSB+IHN0dWRpZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJtdWx0aXBsZSIpKSwgYnkgPSBjKCJsYWJlbCIgPSAibGVpZGVuMyIpKSAKCnAgKyBsYXlvdXRfZGVuZHJvZ3JhbSgpICsKICBnZW9tX3RpcGxhYihhZXMobGFiZWwgPSBwYXN0ZShsYWJlbCwgbU1DVCwgc3R1ZGllcywgc2VwID0gJyAtICcpLCBjb2xvciA9IG1DVCkpICsgCiAgZ2VvbV90aXBwb2ludChhZXMoc2hhcGUgPSBzdHVkaWVzKSwgc2l6ZT0gMykgKwogIHRoZW1lX2RlbmRyb2dyYW0ocGxvdC5tYXJnaW49bWFyZ2luKDE2LDE2LDMwMCwxNikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpKSAlPiUgdW5uYW1lKCkpICsgCiAgZ3VpZGVzKGNvbG9yPSJub25lIikKCgoKcCA8LSBnZ3RyZWUoaGNsdXN0X3NpbSkKcCRkYXRhIDwtIHAkZGF0YSAlPiUgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzICU+JSBtdXRhdGUoc3R1ZGllcyA9IGNhc2Vfd2hlbihzdHVkeVJhdGlvID09MSB+IHN0dWRpZXNSYXRpbywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIm11bHRpcGxlIikpLCBieSA9IGMoImxhYmVsIiA9ICJsZWlkZW4zIikpIAoKcCArIGxheW91dF9kZW5kcm9ncmFtKCkgKwogIGdlb21fdGlwbGFiKGFlcyhsYWJlbCA9IHBhc3RlKGxhYmVsLCBtTUNULCBzdHVkaWVzLCBzZXAgPSAnIC0gJyksIGNvbG9yID0gbUNUKSkgKyAKICBnZW9tX3RpcHBvaW50KGFlcyhzaGFwZSA9IHN0dWRpZXMpLCBzaXplPSAzKSArCiAgdGhlbWVfZGVuZHJvZ3JhbShwbG90Lm1hcmdpbj1tYXJnaW4oMTYsMTYsMzAwLDE2KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjpnbGFzYmV5KCkpICU+JSB1bm5hbWUoKSkgKyAKICBndWlkZXMoY29sb3I9Im5vbmUiKQoKCmBgYAoKIyBDYWxsIENUCgojIyBwYXBlcnMvZmlncyB1c2VkOgoKLSBodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTU5OC0wMjAtNjYwOTItOS9maWd1cmVzLzEKLSBodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTQ2Ny0wMjEtMjU5NjgtOC9maWd1cmVzLzEKLSBodHRwczovL3N0YXRpYy1jb250ZW50LnNwcmluZ2VyLmNvbS9lc20vYXJ0JTNBMTAuMTAzOCUyRnM0MTQ2Ny0wMjEtMjU5NjgtOC9NZWRpYU9iamVjdHMvNDE0NjdfMjAyMV8yNTk2OF9NT0VTTTFfRVNNLnBkZgotIGh0dHBzOi8vd3d3LmVtYm9wcmVzcy5vcmcvZG9pL2Z1bGwvMTAuMTUyNTIvZW1iai4yMDE4MTAwODExCi0gaHR0cHM6Ly93d3cucG5hcy5vcmcvZG9pL2Z1bGwvMTAuMTA3My9wbmFzLjIwMDEyNTAxMTcKLSBodHRwczovL3d3dy5wbmFzLm9yZy9kb2kvZnVsbC8xMC4xMDczL3BuYXMuMjIwMDkxNDExOQotIGh0dHBzOi8vd3d3LnBuYXMub3JnL2RvaS8xMC4xMDczL3BuYXMuMjMwNjE1MzEyMAoKYGBge3J9CiMgdG8gImVyYXNlIiBjZWxsIHR5cGUgbGFiZWxzIGluIGEgY2x1c3RlcgpzdXNfbm9ubmV1cmFsIDwtIGxpc3QoKQojIHRvICJyZW5hbWUiIGNlbGwgdHlwZSBsYWJlbCBpbiBhIGNsdXN0ZXIKcmVsYWJlbF9ub25uZXVyYWwgPC0gbGlzdCgpCiMgdG8gcHJvdmlkZSBhbiBhZGRpdGlvbmFsIGxheWVyIG9mIHJlc29sdXRpb24gdG8gdGhlIGNlbGwgdHlwZQpocl9ub25uZXVyYWwgPC0gbGlzdCgpCmBgYAoKIyMgcnBlIG1hcmtlcnMKCmFsbCBycGUgY2x1c3RlcnMgZXhwcmVzcyBoaWdoIEJFU1QxL1JQRTY1CmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9NX0KdGliIDwtIGRpZmZfbm9ubmV1cmFsJGRpZmZfdGVzdGluZyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9IGMoJ2Jhc2UnPSdsZWlkZW4zJykpICU+JSAKICAjZmlsdGVyKGdyZXBsKCJycGV8bXVlbGxlcnxtZWxhbm8iLG1NQ1QpKSAlPiUgCiAgbGVmdF9qb2luKGNvbnZfdGFibGUpICU+JSAKICBmaWx0ZXIoU1lNQk9MICVpbiUgYygiQkVTVDEiLCJSUEU2NSIsICJQTEQ1IiwiTkVERDRMIiwiT1RYMiIsIk9QQ01MIiwiSU5UMSIpKSAlPiUgCiAgbXV0YXRlKGJhc2UgPSBhcy5jaGFyYWN0ZXIoYmFzZSksCiAgICAgICAgIGJhc2UgPSBjYXNlX3doZW4oZ3JlcGwoInJwZSIsIG1NQ1QpIH4gcGFzdGUwKGJhc2UsICcgLSAnLCBtTUNUKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gYmFzZSkpICU+JSAKICBzZWxlY3QoU1lNQk9MLCBiYXNlLCBsb2dmb2xkY2hhbmdlcykgJT4lIAogIHBpdm90X3dpZGVyKHZhbHVlc19mcm9tID0gbG9nZm9sZGNoYW5nZXMsIG5hbWVzX2Zyb20gPSBiYXNlKQoKbWF0IDwtIHRpYiAlPiUgc2VsZWN0KC0xKSAlPiUgYXMubWF0cml4KCkKcm93Lm5hbWVzKG1hdCkgPC0gdGliICU+JSBwdWxsKDEpCgpDVCA9IGNvbG5hbWVzKHRpYilbLTFdICU+JSAKICBnc3ViKCdcXGQrIC0gJywnJywgLikgCgojbmFtZXMoQ1QpIDwtIGMocGFsczo6YWxwaGFiZXQoKSxwYWxzOjpnbGFzYmV5KCkpCgpoYV9jb2x1bW4gPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKAogIGRmID0gCiAgICBkYXRhLmZyYW1lKENUKQopCgpjb2xfZnVuID0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygtNiwgMCwgNiksIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCkNvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKG1hdCwgY29sPWNvbF9mdW4sCiAgICAgICAgICAgICAgICAgICAgICAgIHRvcF9hbm5vdGF0aW9uID0gaGFfY29sdW1uLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ2xvZ0ZvbGRDaGFuZ2UnKQoKIyBocl9ub25uZXVyYWwkYHJwZSAob3R4Mi0sIG9wY21sLSlgIDwtIGMoMzMpCiMgaHJfbm9ubmV1cmFsJGBycGUgKG90eDIrLCBvcGNtbC0pYCA8LSBjKDc3LDExMykKIyBocl9ub25uZXVyYWwkYHJwZSAoKWAKYGBgCgpgYGB7cn0Kb2JzX25vbm5ldXJhbCRvYnMgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBmaWx0ZXIoZ3JlcGwoInJwZSIsIG1NQ1QpKSAlPiUgCiAgZ2dwbG90KGFlcyh4PXVtYXAxLHk9dW1hcDIpKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoYWVzKGNvbG9yID0gbU1DVCksIHBvaW50c2l6ZSA9IDAuOCwgYWxwaGEgPSAwLjUpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShkYXRhID0gLiAlPiUgZmlsdGVyKGxlaWRlbjMgJWluJSBzdXNfbm9ubmV1cmFsJG11ZWxsZXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gJ3JlZCcsIHBvaW50c2l6ZSA9IDAuOCwgYWxwaGEgPSAwLjUpICsKICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSAuICU+JSBncm91cF9ieShsZWlkZW4zKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bWFwMiA9IG1lZGlhbih1bWFwMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gbGVpZGVuMykpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpKSAlPiUgdW5uYW1lKCkpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpIApgYGAKCiMjIG11ZWxsZXIgbWFya2VycwoKaHR0cHM6Ly9wbWMubmNiaS5ubG0ubmloLmdvdi9hcnRpY2xlcy9QTUMyNjY1MjYzLwoKTW9zdCBtdWVsbGVyIGNsdXN0ZXJzIGV4cHJlc3Mga25vd24gbWFya2VycyAtIHR3byBoYXZlIGxvdyBleHByZXNzaW9uIG9mIG1vc3QgYW5kIGFyZSBsaWtlbHkgdG8gYmUgcmVtb3ZlZC4KCjQ0LDcxIGFyZSBsaWtlbHkgdW5pcXVlIG9uIHRoZSBVTUFQIHZpZXcgYmVjYXVzZSBvZiBBS0FQNCBhbmQgTUFQNkQxIChtYXJrZXJzIGluIGRpZmYgdGVzdGluZy4uLm5vdCBjZXJ0YWluIHdoYXQgc2lnbmlmaWNhbmNlIHRoZXNlIGhhdmUpCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9NX0KdGliIDwtIGRpZmZfbm9ubmV1cmFsJGRpZmZfdGVzdGluZyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9IGMoJ2Jhc2UnPSdsZWlkZW4zJykpICU+JSAKICAjZmlsdGVyKGdyZXBsKCJycGV8bXVlbGxlcnxtZWxhbm8iLG1NQ1QpKSAlPiUgCiAgbGVmdF9qb2luKGNvbnZfdGFibGUpICU+JSAKICBmaWx0ZXIoU1lNQk9MICVpbiUgYygiUkxCUDEiLCJTTEMxQTMiLCJTT1gyIiwiQ1JBQlAxIiwiREtLMyIsICJHUFIzNyIsICdHQUcxMkInICwnTUFQNkQxJywnQUtBUDQnKSkgJT4lIAogIG11dGF0ZShiYXNlID0gYXMuZmFjdG9yKGJhc2UpKSAlPiUgCiAgbXV0YXRlKGJhc2UgPSBjYXNlX3doZW4oZ3JlcGwoIm11ZWxsZXIiLCBtTUNUKSB+IHBhc3RlMChiYXNlLCAnIC0gJywgbU1DVCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IGJhc2UpKSAlPiUgCiAgc2VsZWN0KFNZTUJPTCwgYmFzZSwgbG9nZm9sZGNoYW5nZXMpICU+JSAKICBwaXZvdF93aWRlcih2YWx1ZXNfZnJvbSA9IGxvZ2ZvbGRjaGFuZ2VzLCBuYW1lc19mcm9tID0gYmFzZSkKCm1hdCA8LSB0aWIgJT4lIHNlbGVjdCgtMSkgJT4lIGFzLm1hdHJpeCgpCnJvdy5uYW1lcyhtYXQpIDwtIHRpYiAlPiUgcHVsbCgxKQoKQ1QgPSBjb2xuYW1lcyh0aWIpWy0xXSAlPiUgCiAgZ3N1YignXFxkKyAtICcsJycsIC4pIAoKI25hbWVzKENUKSA8LSBjKHBhbHM6OmFscGhhYmV0KCkscGFsczo6Z2xhc2JleSgpKQoKaGFfY29sdW1uIDwtIENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwQW5ub3RhdGlvbigKICBkZiA9IAogICAgZGF0YS5mcmFtZShDVCkKKQoKY29sX2Z1biA9IGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTYsIDAsIDYpLCBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQpDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChtYXQsIGNvbD1jb2xfZnVuLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ2xvZ0ZvbGRDaGFuZ2UnKQoKc3VzX25vbm5ldXJhbCRtdWVsbGVyIDwtIGMoNjYsOTUpCmBgYApgYGB7cn0Kb2JzX25vbm5ldXJhbCRvYnMgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBmaWx0ZXIoZ3JlcGwoIm11ZWxsZXIiLCBtTUNUKSkgJT4lIAogIGdncGxvdChhZXMoeD11bWFwMSx5PXVtYXAyKSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGFlcyhjb2xvciA9IG1NQ1QpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMC41KSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoZGF0YSA9IC4gJT4lIGZpbHRlcihsZWlkZW4zICVpbiUgc3VzX25vbm5ldXJhbCRtdWVsbGVyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICdyZWQnLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMC41KSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gLiAlPiUgZ3JvdXBfYnkobGVpZGVuMykgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UodW1hcDEgPSBtZWRpYW4odW1hcDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW1hcDIgPSBtZWRpYW4odW1hcDIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IGxlaWRlbjMpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSAKYGBgCgoKIyMgYXN0cm9jeXRlIG1hcmtlcnMKCkFzIGFzdHJvY3l0ZXMgYXJlIHZlcnkgc2ltaWxhciB0byBNdWVsbGVyIChhbmQgaGF2ZSBzb21lIG92ZXJsYXBwaW5nIG1hcmtlciBnZW5lcykgSSBhbSBsYWJlbGxpbmcgYm90aCB0eXBlcyBvZiBjbHVzdGVycy4gR0ZBUCBpcyB0aGUgb25lICJjYW5vbmljYWwiIGFzdHJvY3l0ZSBtYXJrZXIuIEFsc28gdG9zc2luZyBpbiB0aGUgY2Fub25pY2FsIFJMQlAxIChtdWVsbGVyKSBtYXJrZXIgdG8gY2hlY2sgZm9yIG1peGVkIGNsdXN0ZXJzLgoKT2RkbHkgZW5vdWdoIGhhbGYgb2YgdGhlIGFzdHJvY3l0ZSBjbHVzdGVycyBhcmUgbG93IEdGQVAuLi4ud2hpY2ggaXMgY29uc2lkZXJlZCBhIGNhbm9uaWNhbCBtYXJrZXIuIFRoaXMgcmV2aWV3IChodHRwczovL3d3dy5yZXNlYXJjaGdhdGUubmV0L3B1YmxpY2F0aW9uLzM1NDYxMjgwOF9CZXlvbmRfdGhlX0dGQVAtQXN0cm9jeXRlX1Byb3RlaW5fTWFya2Vyc19pbl90aGVfQnJhaW4vbGluay82MTVjM2E0Y2MwNGY1OTA5ZmQ3ZGQ5YWUvZG93bmxvYWQ/X3RwPWV5SmpiMjUwWlhoMElqcDdJbVpwY25OMFVHRm5aU0k2SW5CMVlteHBZMkYwYVc5dUlpd2ljR0ZuWlNJNkluQjFZbXhwWTJGMGFXOXVJbjE5KSBzYXlzIEZNTjIgYW5kIE5FQkwgYXJlIGFsc28gaGlnaCBpbiBicmFpbiBhc3Ryb2N5dGVzIGFuZCBpbmRlZWQgdGhlc2UgbG93IEdGQVAgYXN0cm9jeXRlIGFyZSBoaWdoIGluIHRoZXNlLiBTbyBJJ20gYWxzbyBwcm9wb3NzaW5nIGEgInN1YiBsYWJlbCIgY2xhc3NpZmljYXRpb24gb2YgaGlnaCBHRkFQIGFuZCBoaWdoIEZNTjIgYXN0cm9jeXRlcy4gCgpyZWxhYmVsbGluZyA2NyBhcmUgYW4gYXN0cm9jeXRlIGFsc28uCgpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTd9CnRpYiA8LSBkaWZmX25vbm5ldXJhbCRkaWZmX3Rlc3RpbmcgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSBjKCdiYXNlJz0nbGVpZGVuMycpKSAlPiUgCiAgI2ZpbHRlcihncmVwbCgicnBlfG11ZWxsZXJ8bWVsYW5vIixtTUNUKSkgJT4lIAogIGxlZnRfam9pbihjb252X3RhYmxlKSAlPiUgCiAgZmlsdGVyKFNZTUJPTCAlaW4lIGMoIkdGQVAiLCJSTEJQMSIsJ0ZNTjInLCJORUJMIikpICU+JSAKICBtdXRhdGUoYmFzZSA9IGFzLmZhY3RvcihiYXNlKSkgJT4lIAogIG11dGF0ZShiYXNlID0gY2FzZV93aGVuKGdyZXBsKCJhc3Ryb3xtdWVsbGVyIiwgbU1DVCkgfiBwYXN0ZTAoYmFzZSwgJyAtICcsIG1NQ1QpLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBiYXNlKSkgJT4lIAogIHNlbGVjdChTWU1CT0wsIGJhc2UsIGxvZ2ZvbGRjaGFuZ2VzKSAlPiUgCiAgcGl2b3Rfd2lkZXIodmFsdWVzX2Zyb20gPSBsb2dmb2xkY2hhbmdlcywgbmFtZXNfZnJvbSA9IGJhc2UpCgptYXQgPC0gdGliICU+JSBzZWxlY3QoLTEpICU+JSBhcy5tYXRyaXgoKQpyb3cubmFtZXMobWF0KSA8LSB0aWIgJT4lIHB1bGwoMSkKCkNUID0gY29sbmFtZXModGliKVstMV0gJT4lIAogIGdzdWIoJ1xcZCsgLSAnLCcnLCAuKSAKCiNuYW1lcyhDVCkgPC0gYyhwYWxzOjphbHBoYWJldCgpLHBhbHM6OmdsYXNiZXkoKSkKCmhhX2NvbHVtbiA8LSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oCiAgZGYgPSAKICAgIGRhdGEuZnJhbWUoQ1QpCikKCmNvbF9mdW4gPSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC01LCAwLDUpLCBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQpDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChtYXQsIGNvbD1jb2xfZnVuLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ2xvZ0ZvbGRDaGFuZ2UnKQoKc3VzX25vbm5ldXJhbCRhc3Ryb2N5dGUgPC0gYyg0MiwyLDU2LDUyKQoKcmVsYWJlbF9ub25uZXVyYWwkYXN0cm9jeXRlIDwtIGMoNjcpCgoKaHJfbm9ubmV1cmFsJGBhc3Ryb2N5dGUgKGZtbjIrKWAgPC0gYygyLDQyLDUyLDU2KQpocl9ub25uZXVyYWwkYGFzdHJvY3l0ZSAoZ2ZhcCspYCA8LSBjKDM3LDI4LDU1LDY3KQpgYGAKCmBgYHtyfQpvYnNfbm9ubmV1cmFsJG9icyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9ICdsZWlkZW4zJykgJT4lIAogIGZpbHRlcihncmVwbCgiYXN0cm8iLCBtQ1QpKSAlPiUgCiAgbGVmdF9qb2luKGhyX25vbm5ldXJhbCAlPiVlbmZyYW1lKG5hbWUgPSAnQ1QnLCB2YWx1ZSA9ICdsZWlkZW4zJykgJT4lIHVubmVzdChsZWlkZW4zKSkgJT4lIAogIGdncGxvdChhZXMoeD11bWFwMSx5PXVtYXAyKSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGFlcyhjb2xvciA9IENUKSwgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDEpICsKICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSAuICU+JSBncm91cF9ieShsZWlkZW4zKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bWFwMiA9IG1lZGlhbih1bWFwMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gbGVpZGVuMykpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpKSAlPiUgdW5uYW1lKCkpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpIApgYGAKIyMgZmlicm9ibGFzdHMgKGFuZCBiZWFtIGFuZCBqY3QpCgpSZWFkIG92ZXIgdGhlIHZhbiBaeWwsIFNhbmVzIG91dGxvdyB0cmFjdCBwYXBlciAoaHR0cHM6Ly93d3cucG5hcy5vcmcvZG9pL2Z1bGwvMTAuMTA3My9wbmFzLjIwMDEyNTAxMTcpIGFuZC4uLi5iYXNlZCBvbiB3aGF0IEkgc2VlIGhlcmUgSSBkb24ndCB0aGluayB0aGUgYmVhbSBjZWxscyBhcmUgYSB1bmlxdWUgY2VsbCB0eXBlLi4udGhleSBqdXN0IGxvb2sgbGlrZSBmaWJyb2JsYXN0cy4gU28gSSdtIGdvaW5nIHRvIGRyb3AgdGhlIGJlYW0gbGFiZWwgYW5kIGNhbGwgdGhlbSBmaWJyb2JsYXN0cy4gSkNUIGlzIEFOR1BUTDcgLSByZWxhYmVsbGluZyBjbHVzdGVyIDY1IHRvIEpDVCBhcyBpdCBkb2Vzbid0IGhhdmUgYW55IHN0cm9uZyBmaWJyb2JsYXN0IG1hcmtlciBzaWduYXR1cmUgYW5kIGFkZGluZyBpbiAxMDggYXMgdGhleSBhcmUgbmV4dCB0byBlYWNoIG90aGVyIGluIHRoZSBoY2x1c3QuCgpgYGB7ciwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9MTR9CmEgPC0gb2JzX25vbm5ldXJhbCRvYnMgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBmaWx0ZXIoIE1DVF9zY0FOVklfc3RlcDQgPT0gJ2ZpYnJvYmxhc3QnKSAlPiUgCiAgbXV0YXRlKHRpc3N1ZSA9IGNhc2Vfd2hlbihncmVwbCgiUlBFfENobyIsIHRpc3N1ZSkgfiAiUlBFLUNob3JvaWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJDb3JuZWEiLCB0aXNzdWUpIH4gIkNvcm5lYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIklyaXMiLCB0aXNzdWUpIH4gIklyaXMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB0aXNzdWUpKSAlPiUgCiAgZ2dwbG90KGFlcyh4PXVtYXAxLHk9dW1hcDIpKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoYWVzKGNvbG9yID0gdGlzc3VlKSwgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDEpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpKSAlPiUgdW5uYW1lKCkpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpCmIgPC0gb2JzX25vbm5ldXJhbCRvYnMgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBmaWx0ZXIoIE1DVF9zY0FOVklfc3RlcDQgPT0gJ2ZpYnJvYmxhc3QnKSAlPiUgCiAgbXV0YXRlKHRpc3N1ZSA9IGNhc2Vfd2hlbihncmVwbCgiUlBFfENobyIsIHRpc3N1ZSkgfiAiUlBFLUNob3JvaWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJDb3JuZWEiLCB0aXNzdWUpIH4gIkNvcm5lYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIklyaXMiLCB0aXNzdWUpIH4gIklyaXMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB0aXNzdWUpKSAlPiUgCiAgZ2dwbG90KGFlcyh4PXVtYXAxLHk9dW1hcDIpKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoYWVzKGNvbG9yID0gYV9SYXRpb19zdW0pLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkKYyA8LSBvYnNfbm9ubmV1cmFsJG9icyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9ICdsZWlkZW4zJykgJT4lIAogIGZpbHRlciggTWFqb3JDZWxsVHlwZSA9PSAnZmlicm9ibGFzdCcpICU+JSAKICBtdXRhdGUodGlzc3VlID0gY2FzZV93aGVuKGdyZXBsKCJSUEV8Q2hvIiwgdGlzc3VlKSB+ICJSUEUtQ2hvcm9pZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIkNvcm5lYSIsIHRpc3N1ZSkgfiAiQ29ybmVhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiSXJpcyIsIHRpc3N1ZSkgfiAiSXJpcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IHRpc3N1ZSkpICU+JSAKICBnZ3Bsb3QoYWVzKHg9dW1hcDEseT11bWFwMikpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShhZXMoY29sb3IgPSB0aXNzdWUpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjpnbGFzYmV5KCkpICU+JSB1bm5hbWUoKSkgKyAKICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkKY293cGxvdDo6cGxvdF9ncmlkKGEsYiwgYywgbnJvdyA9MSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTIyLCBmaWcuaGVpZ2h0PTZ9CnRpYiA8LSBkaWZmX25vbm5ldXJhbCRkaWZmX3Rlc3RpbmcgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSBjKCdiYXNlJz0nbGVpZGVuMycpKSAlPiUgCiAgI2ZpbHRlcihncmVwbCgicnBlfG11ZWxsZXJ8bWVsYW5vIixtTUNUKSkgJT4lIAogIGxlZnRfam9pbihjb252X3RhYmxlKSAlPiUgCiAgI2ZpbHRlcihTWU1CT0wgJWluJSBjKCJNR1AiLCJNWU9DIiwiTUVHMyIsIkRDTiIsIkFQT0QiLCJBTkdQVEw3IiwiRUZFTVAxIiwiQk1QNSIsIlBSUlgxIikpICU+JSAKICBmaWx0ZXIoU1lNQk9MICVpbiUgYygiTFVNIiwiRENOIiwiVklNIiwiUERHRlJBIiwiQ09MMUEyIiwgIyBodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MjAwMy0wMjAtMDkyMi00CiAgICAgICAgICAgICAgICAgICAgICAgIk1HUCIsIk1ZT0MiLCJNRUczIiwiRENOIiwiQVBPRCIsIkFOR1BUTDciLCJFRkVNUDEiLCJCTVA1IiwiUFJSWDEiKSkgJT4lIAogIG11dGF0ZShiYXNlID0gYXMuZmFjdG9yKGJhc2UpKSAlPiUgCiAgbXV0YXRlKGJhc2UgPSBjYXNlX3doZW4oZ3JlcGwoImZpYnJvfGJlYW18amN0fHNjaHdhIiwgbU1DVCkgfiBwYXN0ZTAoYmFzZSwgJyAtICcsIG1NQ1QpLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBiYXNlKSkgJT4lIAogIHNlbGVjdChTWU1CT0wsIGJhc2UsIGxvZ2ZvbGRjaGFuZ2VzKSAlPiUgCiAgcGl2b3Rfd2lkZXIodmFsdWVzX2Zyb20gPSBsb2dmb2xkY2hhbmdlcywgbmFtZXNfZnJvbSA9IGJhc2UpCgptYXQgPC0gdGliICU+JSBzZWxlY3QoLTEpICU+JSBhcy5tYXRyaXgoKQpyb3cubmFtZXMobWF0KSA8LSB0aWIgJT4lIHB1bGwoMSkKCkNUID0gY29sbmFtZXModGliKVstMV0gJT4lIAogIGdzdWIoJ1xcZCsgLSAnLCcnLCAuKSAKCiNuYW1lcyhDVCkgPC0gYyhwYWxzOjphbHBoYWJldCgpLHBhbHM6OmdsYXNiZXkoKSkKCmhhX2NvbHVtbiA8LSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oCiAgZGYgPSAKICAgIGRhdGEuZnJhbWUoQ1QpCikKCmNvbF9mdW4gPSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC02LCAwLCA2KSwgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKQ29tcGxleEhlYXRtYXA6OkhlYXRtYXAobWF0LCBjb2w9Y29sX2Z1biwKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdsb2dGb2xkQ2hhbmdlJyxjb2x1bW5fbmFtZXNfbWF4X2hlaWdodD11bml0KDEyLCAiY20iKSkKCgpzdXNfbm9ubmV1cmFsJGZpYnJvYmxhc3QgPC0gYygxNCwxMjEpCgojcmVsYWJlbF9ub25uZXVyYWwgPC0gbGlzdCgpCgpyZWxhYmVsX25vbm5ldXJhbCRqY3QgPC0gYyg2NSwxMDgpCgpvYnNfbm9ubmV1cmFsJGxhYmVscyAlPiUgZmlsdGVyKGxlaWRlbjMgJWluJSBjKHN1c19ub25uZXVyYWwkZmlicm9ibGFzdCwgcmVsYWJlbF9ub25uZXVyYWwkamN0KSkgJT4lIGRhdGEuZnJhbWUgJT4lIHNlbGVjdChsZWlkZW4zLCBtTUNULCBUb3RhbENvdW50LCBzdWJDVCwgc3ViQ291bnQpIApgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpwIDwtIGdndHJlZShoY2x1c3Rfc2ltKQpwJGRhdGEgPC0gcCRkYXRhICU+JSAKICBsZWZ0X2pvaW4ob2JzX25vbm5ldXJhbCRsYWJlbHMgJT4lIAogICAgICAgICAgICAgIG11dGF0ZShub3RlID0gY2FzZV93aGVuKGxlaWRlbjMgJWluJSBzdXNfbm9ubmV1cmFsJGZpYnJvYmxhc3QgIH4gJ3N1c19ub25uZXVyYWwnKSksIGJ5ID0gYygibGFiZWwiID0gImxlaWRlbjMiKSkgCgpwICsgbGF5b3V0X2RlbmRyb2dyYW0oKSArCiAgZ2VvbV90aXBsYWIoYWVzKGxhYmVsID0gcGFzdGUobGFiZWwsIG1NQ1QsIHNlcCA9ICcgLSAnKSwgY29sb3IgPSBtQ1QpKSArIAogIHRoZW1lX2RlbmRyb2dyYW0ocGxvdC5tYXJnaW49bWFyZ2luKDE2LDE2LDMwMCwxNikpICsKICBnZW9tX3RpcHBvaW50KGFlcyhzaGFwZSA9IG5vdGUpLCBzaXplPSA2KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArIAogIGd1aWRlcyhjb2xvcj0ibm9uZSIpCgpgYGAKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04fQpvYnNfbm9ubmV1cmFsJG9icyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9ICdsZWlkZW4zJykgJT4lIAogIGxlZnRfam9pbihyZWxhYmVsX25vbm5ldXJhbCAlPiUgZW5mcmFtZShuYW1lID0gJ25ld0NUJywgdmFsdWUgPSAnbGVpZGVuMycpICU+JSB1bm5lc3QobGVpZGVuMyksIAogICAgICAgICAgICBieSA9ICdsZWlkZW4zJykgJT4lIAogIG11dGF0ZShtQ1QgPSBjYXNlX3doZW4oIWlzLm5hKG5ld0NUKSB+IG5ld0NULAogICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IG1DVCkpICU+JSAKICBmaWx0ZXIoZ3JlcGwoImJlYW18Zmlicm98amN0IiwgbU1DVCkpICU+JSAKICBtdXRhdGUobUNUID0gY2FzZV93aGVuKG1DVCA9PSAnYmVhbScgfiAnZmlicm9ibGFzdCcsCiAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gbUNUKSkgJT4lIAogIGdncGxvdChhZXMoeD11bWFwMSx5PXVtYXAyKSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGFlcyhjb2xvciA9IG1DVCksIHBvaW50c2l6ZSA9IDAuOCwgYWxwaGEgPSAxKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoZGF0YSA9IC4gJT4lIGZpbHRlcihsZWlkZW4zICVpbiUgc3VzX25vbm5ldXJhbCRmaWJyb2JsYXN0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICdyZWQnLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMSkgKwogIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IC4gJT4lIGdyb3VwX2J5KGxlaWRlbjMpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHVtYXAxID0gbWVkaWFuKHVtYXAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBsZWlkZW4zKSwgbWF4Lm92ZXJsYXBzID0gSW5mKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSAKYGBgCgojIyBzY2h3YW5uCgpEcm9wIHRocmVlIHNjaHdhbm4gY2x1c3RlcnMgdGhhdCBlaXRoZXIgbGFjayBtYXJrZXJzIG9yIGFwcGVhciB0byBiZSBtaXhlZCB3aXRoIG90aGVyIGNlbGwgdHlwZSBzaWduYXR1cmVzCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9Nn0KCgp0aWIgPC0gZGlmZl9ub25uZXVyYWwkZGlmZl90ZXN0aW5nICU+JSAKICBsZWZ0X2pvaW4ob2JzX25vbm5ldXJhbCRsYWJlbHMsIGJ5ID0gYygnYmFzZSc9J2xlaWRlbjMnKSkgJT4lIAogICNmaWx0ZXIoZ3JlcGwoInNjaHdhIixtTUNUKSkgJT4lIAogIGxlZnRfam9pbihjb252X3RhYmxlICU+JSBzZWxlY3QoU1lNQk9MLEVOU0VNQkwpICU+JSB1bmlxdWUoKSkgJT4lIAogIGZpbHRlcihTWU1CT0wgJWluJSBjKCJDRDkiLCJQTFAxIiwiTEdJNCIpKSAlPiUgCiAgbXV0YXRlKGJhc2UgPSBhcy5mYWN0b3IoYmFzZSkpICU+JSAKICBtdXRhdGUoYmFzZSA9IGNhc2Vfd2hlbihncmVwbCgic2Nod2EiLCBtTUNUKSB+IHBhc3RlMChiYXNlLCAnIC0gJywgbU1DVCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IGJhc2UpKSAlPiUgCiAgc2VsZWN0KFNZTUJPTCwgYmFzZSwgbG9nZm9sZGNoYW5nZXMpICU+JSAKICBwaXZvdF93aWRlcih2YWx1ZXNfZnJvbSA9IGxvZ2ZvbGRjaGFuZ2VzLCBuYW1lc19mcm9tID0gYmFzZSkKCm1hdCA8LSB0aWIgJT4lIHNlbGVjdCgtMSkgJT4lIGFzLm1hdHJpeCgpCnJvdy5uYW1lcyhtYXQpIDwtIHRpYiAlPiUgcHVsbCgxKQoKQ1QgPSBjb2xuYW1lcyh0aWIpWy0xXSAlPiUgCiAgZ3N1YignXFxkKyAtICcsJycsIC4pIAoKI25hbWVzKENUKSA8LSBjKHBhbHM6OmFscGhhYmV0KCkscGFsczo6Z2xhc2JleSgpKQoKaGFfY29sdW1uIDwtIENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwQW5ub3RhdGlvbigKICBkZiA9IAogICAgZGF0YS5mcmFtZShDVCkKKQoKY29sX2Z1biA9IGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTYsIDAsIDYpLCBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQpDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChtYXQsIGNvbD1jb2xfZnVuLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ2xvZ0ZvbGRDaGFuZ2UnKQoKc3VzX25vbm5ldXJhbCRzY2h3YW5uIDwtIGMoMTAyLDEyNSwxMTYpCgpvYnNfbm9ubmV1cmFsJGxhYmVscyAlPiUgZmlsdGVyKGxlaWRlbjMgJWluJSBjKHN1c19ub25uZXVyYWwkc2Nod2FubiApKSAlPiUgZGF0YS5mcmFtZSAlPiUgc2VsZWN0KGxlaWRlbjMsIG1NQ1QsIFRvdGFsQ291bnQsIHN1YkNULCBzdWJDb3VudCkgCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04fQpvYnNfbm9ubmV1cmFsJG9icyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9ICdsZWlkZW4zJykgJT4lIAogIGZpbHRlcihncmVwbCgic2Nod2EiLCBtQ1QpKSAlPiUgCiAgZ2dwbG90KGFlcyh4PXVtYXAxLHk9dW1hcDIpKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoYWVzKGNvbG9yID0gbUNUKSwgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDEpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShkYXRhID0gLiAlPiUgZmlsdGVyKGxlaWRlbjMgJWluJSBzdXNfbm9ubmV1cmFsJHNjaHdhbm4gICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAncmVkJywgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDEpICsKICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSAuICU+JSBncm91cF9ieShsZWlkZW4zKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bWFwMiA9IG1lZGlhbih1bWFwMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gbGVpZGVuMyksIG1heC5vdmVybGFwcyA9IEluZikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjpnbGFzYmV5KCkpICU+JSB1bm5hbWUoKSkgKyAKICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgCmBgYAoKCiMjIGVwaXRoZWxpYWwgLyBlbmRvdGhlbGlhbAoKYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD02fQoKCnRpYiA8LSBkaWZmX25vbm5ldXJhbCRkaWZmX3Rlc3RpbmcgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSBjKCdiYXNlJz0nbGVpZGVuMycpKSAlPiUgCiAgI2ZpbHRlcihncmVwbCgic2Nod2EiLG1NQ1QpKSAlPiUgCiAgbGVmdF9qb2luKGNvbnZfdGFibGUgJT4lIHNlbGVjdChTWU1CT0wsRU5TRU1CTCkgJT4lIHVuaXF1ZSgpKSAlPiUgCiAgZmlsdGVyKFNZTUJPTCAlaW4lIGMoIkFMUEwiLCJWV0YiLCJDRDU5IiwgI2VuZG8KICAgICAgICAgICAgICAgICAgICAgICAiUE9VNkYyIiwgIk5BTEYxIiwgIlBFQ0FNMSIsICJDTEROMSIsICNlbmRvCiAgICAgICAgICAgICAgICAgICAgICAgIktSVDI0IiwiTU9YRDEiLCAiUEFYNiIsICJLTEY1IiwgIk1FVCIsICJLSVQiLCMgZXBpdGhlbGlhbAogICAgICAgICAgICAgICAgICAgICAgICJBTlhBMSIsICJUR0ZCSSIsIktSVDEyIiwiS1JUMTQiLCAiS1JUMyIsICJLUlQ1IiwgIkNPTDE3QTEiLCAjICJjb3JuZWFsIGVwaXRoZWxpYWwiCiAgICAgICAgICAgICAgICAgICAgICAgIkVTQU0iLCJCQ0FNIiwiR0pBNCIpKSAlPiUgICMgZW5kbwogIG11dGF0ZShiYXNlID0gYXMuZmFjdG9yKGJhc2UpKSAlPiUgCiAgbXV0YXRlKGJhc2UgPSBjYXNlX3doZW4oZ3JlcGwoImVwaXxlbmRvIiwgbU1DVCkgfiBwYXN0ZTAoYmFzZSwgJyAtICcsIG1NQ1QpLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBiYXNlKSkgJT4lIAogIHNlbGVjdChTWU1CT0wsIGJhc2UsIGxvZ2ZvbGRjaGFuZ2VzKSAlPiUgCiAgcGl2b3Rfd2lkZXIodmFsdWVzX2Zyb20gPSBsb2dmb2xkY2hhbmdlcywgbmFtZXNfZnJvbSA9IGJhc2UpCgptYXQgPC0gdGliICU+JSBzZWxlY3QoLTEpICU+JSBhcy5tYXRyaXgoKQpyb3cubmFtZXMobWF0KSA8LSB0aWIgJT4lIHB1bGwoMSkKCkNUID0gY29sbmFtZXModGliKVstMV0gJT4lIAogIGdzdWIoJ1xcZCsgLSAnLCcnLCAuKSAKCiNuYW1lcyhDVCkgPC0gYyhwYWxzOjphbHBoYWJldCgpLHBhbHM6OmdsYXNiZXkoKSkKCmhhX2NvbHVtbiA8LSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oCiAgZGYgPSAKICAgIGRhdGEuZnJhbWUoQ1QpCikKCmNvbF9mdW4gPSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC02LCAwLCA2KSwgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKQ29tcGxleEhlYXRtYXA6OkhlYXRtYXAobWF0LCBjb2w9Y29sX2Z1biwKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdsb2dGb2xkQ2hhbmdlJywgCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19tYXhfaGVpZ2h0ID0gdW5pdCgxMiwnY20nKSkKCiNzdXNfbm9ubmV1cmFsJGVwaXRoZWxpYWwgPC0gYyg4OSkKI3N1c19ub25uZXVyYWwkZW5kb3RoZWxpYWwgPC0gYyg2NCkKcmVsYWJlbF9ub25uZXVyYWwkZW5kb3RoZWxpYWwgPC0gYygyOSwxNCkKCm9ic19ub25uZXVyYWwkbGFiZWxzICU+JSBmaWx0ZXIobGVpZGVuMyAlaW4lIGMoc3VzX25vbm5ldXJhbCRlcGl0aGVsaWFsLCBzdXNfbm9ubmV1cmFsJGVuZG90aGVsaWFsLCByZWxhYmVsX25vbm5ldXJhbCRlbmRvdGhlbGlhbCwgcmVsYWJlbF9ub25uZXVyYWwkZXBpdGhlbGlhbCkpICU+JSBkYXRhLmZyYW1lICU+JSBzZWxlY3QobGVpZGVuMywgbU1DVCwgVG90YWxDb3VudCwgc3ViQ1QsIHN1YkNvdW50KSAlPiUgRFQ6OmRhdGF0YWJsZSgpCmBgYAoKIyMjIGVwaXRoZWxpYWwgaGlnaGVyIHJlc29sdXRpb24KaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DODczMzc3Ni8KCmBgYApUeXBlIEkga2VyYXRpbnMgKEs5LUsyMSwgSzIzLCBIYTEtOCkgYXJlIHNtYWxsZXIgYW5kIGFjaWRpYyBjb21wYXJlZCB0byB0aGUgbGFyZ2VyLCBuZXV0cmFsLWJhc2ljIHR5cGUgSUkga2VyYXRpbnMgKEsxLUs4LCBIYjEtNikuCmBgYAoKYGBge3J9CnRpYiA8LSBkaWZmX25vbm5ldXJhbCRkaWZmX3Rlc3RpbmcgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSBjKCdiYXNlJz0nbGVpZGVuMycpKSAlPiUgCiAgI2ZpbHRlcihncmVwbCgic2Nod2EiLG1NQ1QpKSAlPiUgCiAgbGVmdF9qb2luKGNvbnZfdGFibGUgJT4lIHNlbGVjdChTWU1CT0wsRU5TRU1CTCkgJT4lIHVuaXF1ZSgpKSAlPiUgCiAgI2ZpbHRlcihncmVwbCgiXktSVFxcZCIsIFNZTUJPTCkpICU+JSAKICBmaWx0ZXIoU1lNQk9MICVpbiUgKGh2ZyAlPiUgbGVmdF9qb2luKGNvbnZfdGFibGUsIGJ5ID0gYygnVjInID0gJ0VOU0VNQkwnKSkgJT4lIGZpbHRlcihncmVwbCgiXktSVHxeTEFNQ3xeREVTfF5ORUYiLFNZTUJPTCkpICU+JSBwdWxsKFNZTUJPTCkpKSAlPiUgCiAgZmlsdGVyKGdyZXBsKCJlcGl0aCIsIG1DVCkgfCBiYXNlICVpbiUgcmVsYWJlbF9ub25uZXVyYWwkZXBpdGhlbGlhbCkgJT4lIAogIG11dGF0ZShiYXNlID0gYXMuZmFjdG9yKGJhc2UpKSAlPiUgCiAgbXV0YXRlKGJhc2UgPSBwYXN0ZTAoYmFzZSwgJyAtICcsIG1DVCkpICU+JSAKICBzZWxlY3QoU1lNQk9MLCBiYXNlLCBsb2dmb2xkY2hhbmdlcykgJT4lIAogIHBpdm90X3dpZGVyKHZhbHVlc19mcm9tID0gbG9nZm9sZGNoYW5nZXMsIG5hbWVzX2Zyb20gPSBiYXNlKQoKbWF0IDwtIHRpYiAlPiUgc2VsZWN0KC0xKSAlPiUgYXMubWF0cml4KCkKcm93Lm5hbWVzKG1hdCkgPC0gdGliICU+JSBwdWxsKDEpCgpDVCA9IGNvbG5hbWVzKHRpYilbLTFdICU+JSAKICBnc3ViKCdcXGQrIC0gJywnJywgLikgCgojbmFtZXMoQ1QpIDwtIGMocGFsczo6YWxwaGFiZXQoKSxwYWxzOjpnbGFzYmV5KCkpCgpoYV9jb2x1bW4gPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKAogIGRmID0gCiAgICBkYXRhLmZyYW1lKENUKQopCgpjb2xfZnVuID0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygtNiwgMCwgNiksIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCkNvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKG1hdCwgY29sPWNvbF9mdW4sCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAnbG9nRm9sZENoYW5nZScsIAogICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZXNfbWF4X2hlaWdodCA9IHVuaXQoMTIsJ2NtJykpCgpocl9ub25uZXVyYWwkYGVwaXRoZWxpYWwgKG5vbiBrZXJhdGluaXplZClgIDwtIGMoNiwyNSw4OSkKaHJfbm9ubmV1cmFsJGBlcGl0aGVsaWFsICh0eXBlIGlpKWAgPC0gYyg1LDE3LDcwKQpocl9ub25uZXVyYWwkYGVwaXRoZWxpYWwgKHR5cGUgaSlgIDwtIG9ic19ub25uZXVyYWwkbGFiZWxzICU+JSAKICBmaWx0ZXIobUNUID09ICdlcGl0aGVsaWFsJywgCiAgICAgICAgICFsZWlkZW4zICVpbiUgCiAgICAgICAgICAgKGhyX25vbm5ldXJhbCAlPiUgZW5mcmFtZSgpICU+JSB1bm5lc3QodmFsdWUpICU+JSBwdWxsKHZhbHVlKSkpICU+JSAKICBwdWxsKGxlaWRlbjMpCmBgYAojIyMgZW5kb3RoZWxpYWwgaGlnaGVyIHJlc29sdXRpb24KYGBge3J9CiNQZWNhbTEgKENkMzEpLCBDZGg1IGFuZCBGbHQxIAoKdGliIDwtIGRpZmZfbm9ubmV1cmFsJGRpZmZfdGVzdGluZyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9IGMoJ2Jhc2UnPSdsZWlkZW4zJykpICU+JSAKICBsZWZ0X2pvaW4oY29udl90YWJsZSAlPiUgc2VsZWN0KFNZTUJPTCxFTlNFTUJMKSAlPiUgdW5pcXVlKCkpICU+JSAKICBmaWx0ZXIoU1lNQk9MICVpbiUgYygiUEVDQU0xIiwiRkxUMSIsIlZXRiIsIk1JQ0FMMiIsIkFLVDMiLCJBREdSTDQiLCJaRUIxIiwiQ0NOWSIsIlBJSzNDMkEiKSkgJT4lIAogIGZpbHRlcihncmVwbCgiZW5kb3RoIiwgbUNUKSB8IGJhc2UgJWluJSByZWxhYmVsX25vbm5ldXJhbCRlbmRvdGhlbGlhbCkgJT4lIAogIG11dGF0ZShiYXNlID0gYXMuZmFjdG9yKGJhc2UpKSAlPiUgCiAgbXV0YXRlKGJhc2UgPSBwYXN0ZTAoYmFzZSwgJyAtICcsIG1DVCkpICU+JSAKICBzZWxlY3QoU1lNQk9MLCBiYXNlLCBsb2dmb2xkY2hhbmdlcykgJT4lIAogIHBpdm90X3dpZGVyKHZhbHVlc19mcm9tID0gbG9nZm9sZGNoYW5nZXMsIG5hbWVzX2Zyb20gPSBiYXNlKQoKbWF0IDwtIHRpYiAlPiUgc2VsZWN0KC0xKSAlPiUgYXMubWF0cml4KCkKcm93Lm5hbWVzKG1hdCkgPC0gdGliICU+JSBwdWxsKDEpCgpDVCA9IGNvbG5hbWVzKHRpYilbLTFdICU+JSAKICBnc3ViKCdcXGQrIC0gJywnJywgLikgCgojbmFtZXMoQ1QpIDwtIGMocGFsczo6YWxwaGFiZXQoKSxwYWxzOjpnbGFzYmV5KCkpCgpoYV9jb2x1bW4gPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKAogIGRmID0gCiAgICBkYXRhLmZyYW1lKENUKQopCgpjb2xfZnVuID0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygtNiwgMCwgNiksIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCkNvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKG1hdCwgY29sPWNvbF9mdW4sCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAnbG9nRm9sZENoYW5nZScsIAogICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZXNfbWF4X2hlaWdodCA9IHVuaXQoMTIsJ2NtJykpCgpocl9ub25uZXVyYWwkYGVuZG90aGVsaWFsIChmbHQxKylgIDwtIGMoMTQsMjEsMjksNzYsODEpCmhyX25vbm5ldXJhbCRgZW5kb3RoZWxpYWwgKGZsdDEtKWAgPC0gYygzMSw2NCkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTh9Cm9ic19ub25uZXVyYWwkb2JzICU+JSAKICBsZWZ0X2pvaW4ob2JzX25vbm5ldXJhbCRsYWJlbHMsIGJ5ID0gJ2xlaWRlbjMnKSAlPiUgCiAgbGVmdF9qb2luKGhyX25vbm5ldXJhbCAlPiUgZW5mcmFtZShuYW1lID0gJ25ld0NUJywgdmFsdWUgPSAnbGVpZGVuMycpICU+JSB1bm5lc3QobGVpZGVuMyksIAogICAgICAgICAgICBieSA9ICdsZWlkZW4zJykgJT4lIAogIG11dGF0ZShtQ1QgPSBjYXNlX3doZW4oIWlzLm5hKG5ld0NUKSB+IG5ld0NULAogICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IG1DVCkpICU+JSAKICBmaWx0ZXIoZ3JlcGwoImVwaXxlbmRvIiwgbUNUKSkgJT4lIAogIGdncGxvdChhZXMoeD11bWFwMSx5PXVtYXAyKSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGFlcyhjb2xvciA9IG1DVCksIHBvaW50c2l6ZSA9IDAuOCwgYWxwaGEgPSAxKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoZGF0YSA9IC4gJT4lIGZpbHRlcihsZWlkZW4zICVpbiUgYyhzdXNfbm9ubmV1cmFsJGVuZG90aGVsaWFsLCBzdXNfbm9ubmV1cmFsJGVwaXRoZWxpYWwgICkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gJ3JlZCcsIHBvaW50c2l6ZSA9IDAuOCwgYWxwaGEgPSAxKSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gLiAlPiUgZ3JvdXBfYnkobGVpZGVuMywgbUNUKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bWFwMiA9IG1lZGlhbih1bWFwMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gbGVpZGVuMyksIG1heC5vdmVybGFwcyA9IEluZikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmFscGhhYmV0KCksIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSAKYGBgCgojIyBtZWxhbm9jeXRlcwoKODggZHJvcHBlZCBmb3IgcGlnbWVudGF0aW9uIG1hcmtlcnMgKERDVCwgTUxBTkEsIFRZUikKClR3byBtZWxhbm9jeXRlIGNsdXN0ZXJzIGFyZSBjb25maWRlbnQgYnkgdGhlIG1hY2hpbmUgbGVhcm5pbmcgLyBhdXRob3IgbGFiZWxzIGJ1dCBsYWNrCnBpZ21lbnRhdGlvbiAocHJvYmFibHk/KSBhcyB0aGV5IGhhdmUgbG93IERDVC9NTEFOQS9UWVIgZXhwcmVzc2lvbi4gTGFiZWxsZWQgYXMgdW5waWdtZW50ZWQuCgpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTZ9CgoKdGliIDwtIGRpZmZfbm9ubmV1cmFsJGRpZmZfdGVzdGluZyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9IGMoJ2Jhc2UnPSdsZWlkZW4zJykpICU+JSAKICAjZmlsdGVyKGdyZXBsKCJzY2h3YSIsbU1DVCkpICU+JSAKICBsZWZ0X2pvaW4oY29udl90YWJsZSAlPiUgc2VsZWN0KFNZTUJPTCxFTlNFTUJMKSAlPiUgdW5pcXVlKCkpICU+JSAKICBmaWx0ZXIoU1lNQk9MICVpbiUgYygiTUxBTkEiLCJEQ1QiLCJBUVAxIiwgIlBBWDMiLCJNRVQiLCAiS0lUIiwiTEVGMSIsIlRZUiIsIlRZUlAxIikpICU+JSAgIyBlbmRvCiAgbXV0YXRlKGJhc2UgPSBhcy5mYWN0b3IoYmFzZSkpICU+JSAKICBtdXRhdGUoYmFzZSA9IGNhc2Vfd2hlbihncmVwbCgibWVsYW4iLCBtTUNUKSB+IHBhc3RlMChiYXNlLCAnIC0gJywgbU1DVCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IGJhc2UpKSAlPiUgCiAgc2VsZWN0KFNZTUJPTCwgYmFzZSwgbG9nZm9sZGNoYW5nZXMpICU+JSAKICBwaXZvdF93aWRlcih2YWx1ZXNfZnJvbSA9IGxvZ2ZvbGRjaGFuZ2VzLCBuYW1lc19mcm9tID0gYmFzZSkKCm1hdCA8LSB0aWIgJT4lIHNlbGVjdCgtMSkgJT4lIGFzLm1hdHJpeCgpCnJvdy5uYW1lcyhtYXQpIDwtIHRpYiAlPiUgcHVsbCgxKQoKQ1QgPSBjb2xuYW1lcyh0aWIpWy0xXSAlPiUgCiAgZ3N1YignXFxkKyAtICcsJycsIC4pIAoKI25hbWVzKENUKSA8LSBjKHBhbHM6OmFscGhhYmV0KCkscGFsczo6Z2xhc2JleSgpKQoKaGFfY29sdW1uIDwtIENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwQW5ub3RhdGlvbigKICBkZiA9IAogICAgZGF0YS5mcmFtZShDVCkKKQoKY29sX2Z1biA9IGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTYsIDAsIDYpLCBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQpDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChtYXQsIGNvbD1jb2xfZnVuLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ2xvZ0ZvbGRDaGFuZ2UnLAogICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZXNfbWF4X2hlaWdodCA9IHVuaXQoMTIsJ2NtJykpCgpzdXNfbm9ubmV1cmFsJG1lbGFub2N5dGUgPC0gYyg4OCkKaHJfbm9ubmV1cmFsJGBtZWxhbm9jeXRlICh1bnBpZ21lbnRlZClgIDwtIGMoOTcsOTYpCmhyX25vbm5ldXJhbCRgbWVsYW5vY3l0ZSAocGlnbWVudGVkKWAgPC0gYygyNiwxMTUsMTA5LDQxLDExLDQ3KQoKb2JzX25vbm5ldXJhbCRsYWJlbHMgJT4lIGZpbHRlcihsZWlkZW4zICVpbiUgYyhzdXNfbm9ubmV1cmFsJG1lbGFub2N5dGUpKSAlPiUgZGF0YS5mcmFtZSAlPiUgc2VsZWN0KGxlaWRlbjMsIG1NQ1QsIFRvdGFsQ291bnQsIHN1YkNULCBzdWJDb3VudCkgJT4lIERUOjpkYXRhdGFibGUoKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OH0Kb2JzX25vbm5ldXJhbCRvYnMgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBsZWZ0X2pvaW4oaHJfbm9ubmV1cmFsICU+JSBlbmZyYW1lKG5hbWUgPSAnbmV3Q1QnLCB2YWx1ZSA9ICdsZWlkZW4zJykgJT4lIHVubmVzdChsZWlkZW4zKSwgCiAgICAgICAgICAgIGJ5ID0gJ2xlaWRlbjMnKSAlPiUgCiAgbXV0YXRlKG1DVCA9IGNhc2Vfd2hlbighaXMubmEobmV3Q1QpIH4gbmV3Q1QsCiAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gbUNUKSkgJT4lIAogIGZpbHRlcihncmVwbCgibWVsYW5vIiwgbUNUKSkgJT4lIAogIGdncGxvdChhZXMoeD11bWFwMSx5PXVtYXAyKSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGFlcyhjb2xvciA9IG1DVCksIHBvaW50c2l6ZSA9IDAuOCwgYWxwaGEgPSAxKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoZGF0YSA9IC4gJT4lIGZpbHRlcihsZWlkZW4zICVpbiUgYyhzdXNfbm9ubmV1cmFsJG1lbGFub2N5dGUgKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAncmVkJywgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDEpICsKICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSAuICU+JSBncm91cF9ieShsZWlkZW4zLCBtQ1QpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHVtYXAxID0gbWVkaWFuKHVtYXAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBsZWlkZW4zKSwgbWF4Lm92ZXJsYXBzID0gSW5mKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQoKSwgcGFsczo6Z2xhc2JleSgpKSAlPiUgdW5uYW1lKCkpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpIApgYGAKCgoKIyMgaW1tdW5lCmBgYApodHRwczovL3d3dy5wbmFzLm9yZy9kb2kvZnVsbC8xMC4xMDczL3BuYXMuMjAwMTI1MDExNzogCk91ciBkYXRhc2V0IGFsc28gaW5jbHVkZWQgZm91ciB0eXBlcyBvZiBpbW11bmUgY2VsbHM6IEIgY2VsbHMsIE5hdHVyYWwgS2lsbGVyIChOSykvVCBjZWxscywgbWFzdCBjZWxscywgYW5kIG1hY3JvcGhhZ2VzLiBUaGUgbWFjcm9waGFnZXMgKEM0KSB3ZXJlIENEMTYzKyBhbmQgTFlWRTErICg0OSkgYW5kIGxvY2FsaXplZCBwcmVkb21pbmFudGx5IHRvIHRoZSBUTSAoRmlnLiA0RykuIFRoZXkgYWxzbyBleHByZXNzZWQgQ0Q2OCwgQ0QxNCwgQ0NMMywgQ0NMNCwgQ1hDTDgsIElMMUIsIFRSRU0yLCBhbmQgTVM0QSBnZW5lcywgYWxsIG9mIHdoaWNoIGhhdmUgYmVlbiBhc3NvY2lhdGVkIHdpdGggbWFjcm9waGFnZXMgaW4gb3RoZXIgdGlzc3Vlcy4gTWFzdCBjZWxscyAoQzEzKSB3ZXJlIGxvY2FsaXplZCB0byB0aGUgVE0gdXNpbmcgdGhlIG1hcmtlciBJTDFSTDEgYW5kIGFsc28gZXhwcmVzc2VkIENQQTMsIFJHUzEzLCBhbmQgS0lUIChTSSBBcHBlbmRpeCwgRmlnLiBTMkYpLiBCIGNlbGxzIChDMTQpLCBjaGFyYWN0ZXJpemVkIGJ5IGV4cHJlc3Npb24gb2YgQ0QyNywgQ0Q3OUEsIElHSE0sIElHS0MsIE1aQjEsIGFuZCBKQ0hBSU4sIHdlcmUgZm91bmQgaW4gb25seSBvbmUgZG9ub3Igc2FtcGxlIGJ1dCB3ZXJlIGlkZW50aWZpZWQgaGlzdG9sb2dpY2FsbHkgaW4gdGlzc3VlcyBmcm9tIG90aGVyIGRvbm9ycyB1c2luZyB0aGUgbWFya2VyIENEMjcgKFNJIEFwcGVuZGl4LCBGaWcuIFMyRykuIE5LL1QgY2VsbHMgKGMxMCkgd2VyZSBpZGVudGlmaWVkIGJ5IGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIHRoZSBnZW5lcyBDRDIsIENEM0QsIElMN1IsIFRSQUMsIEdaTUEsIEdaTUIsIGFuZCBOS0c3LgpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9Nn0KaW1tdW5lX2NsdXN0ZXJzIDwtIGMoNzgsMTE3LDE2LDExMCw3NSwzNSw1MCw5Miw4NiwzMCw1OSkKCnRpYiA8LSBkaWZmX25vbm5ldXJhbCRkaWZmX3Rlc3RpbmcgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSBjKCdiYXNlJz0nbGVpZGVuMycpKSAlPiUgCiAgI2ZpbHRlcihncmVwbCgic2Nod2EiLG1NQ1QpKSAlPiUgCiAgbGVmdF9qb2luKGNvbnZfdGFibGUgJT4lIHNlbGVjdChTWU1CT0wsRU5TRU1CTCkgJT4lIHVuaXF1ZSgpKSAlPiUgCiAgZmlsdGVyKFNZTUJPTCAlaW4lIGMoIkxZVkUxIiwiQ0QxNjMiLAogICAgICAgICAgICAgICAgICAgICAgICJDMVFBIiwiQ1RTUyIsIkIyTSIsIkhMQS1EUEExIiwiSExBLURQQjEiLCAiSExBLURSQSIsCiAgICAgICAgICAgICAgICAgICAgICAgIkNEMjciLCJDRDc5QSIsCiAgICAgICAgICAgICAgICAgICAgICAgIkNEMiIsCiAgICAgICAgICAgICAgICAgICAgICAgIklMMVJMMSIsCiAgICAgICAgICAgICAgICAgICAgICAgIkhCQiIsIkhCQSIpKSAlPiUgIyJDMVFBIiwiQ1RTUyIsIkIyTSIsIkhMQS1EUEExIiwiSExBLURQQjEiLCAiSExBLURSQSIsCiAgIyAiUDJSWTEyIiwiVE1FTTExOSIsIlNJR0xFQ0giLCJTRVJJTkMzIiwKICAjICJMUEwiLCJDU1Q3IiwiU1BQMSIsIkNTVEIiLAogICMgIkNDUjEiLCJDQ1IyIikpICU+JSAKICAKICAjYygiQVJHMSIsIkNEODYiLCJUTkYiLCJOT1MyIiwiUkVUTkxBIiwiTUdMMiIsIkNISUwzIiwgI21hY3JvcGhhZ2UKICAjICAiVE1FTTExOSIsIlAyWTEyUiIsIk9MRk1MMyIsIlNBTEwxIiwgIkxZVkUxIiwiQ0QxNjMiLAogICMgICJIQkIiKSkgJT4lICAjbWljcm9nbGlhCiAgIyJDRDY4IiwiQ0QxNCIsICJHQVBESCIsIlBHQU0xIikpICU+JSAjLCAjbWFjcm9waGFnZSkpICU+JSAKICBtdXRhdGUoYmFzZSA9IGFzLmZhY3RvcihiYXNlKSkgJT4lIAogIG11dGF0ZShiYXNlID0gY2FzZV93aGVuKGdyZXBsKCJtaWNyb2d8bW9ub3xtYWNyb3xpbW11bmV8cmVkIiwgbU1DVCkgfiBwYXN0ZTAoYmFzZSwgJyAtICcsIG1NQ1QpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJhc2UgJWluJSBpbW11bmVfY2x1c3RlcnMgfiBwYXN0ZTAoYmFzZSwgJyAtICcsIG1NQ1QpLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBiYXNlKSkgJT4lIAogIHNlbGVjdChTWU1CT0wsIGJhc2UsIGxvZ2ZvbGRjaGFuZ2VzKSAlPiUgCiAgcGl2b3Rfd2lkZXIodmFsdWVzX2Zyb20gPSBsb2dmb2xkY2hhbmdlcywgbmFtZXNfZnJvbSA9IGJhc2UpCgptYXQgPC0gdGliICU+JSBzZWxlY3QoLTEpICU+JSBhcy5tYXRyaXgoKQpyb3cubmFtZXMobWF0KSA8LSB0aWIgJT4lIHB1bGwoMSkKCkNUID0gY29sbmFtZXModGliKVstMV0gJT4lIAogIGdzdWIoJ1xcZCsgLSAnLCcnLCAuKSAKCiNuYW1lcyhDVCkgPC0gYyhwYWxzOjphbHBoYWJldCgpLHBhbHM6OmdsYXNiZXkoKSkKCmhhX2NvbHVtbiA8LSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oCiAgZGYgPSAKICAgIGRhdGEuZnJhbWUoQ1QpCikKCmNvbF9mdW4gPSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC02LCAwLCA2KSwgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKQ29tcGxleEhlYXRtYXA6OkhlYXRtYXAobWF0LCBjb2w9Y29sX2Z1biwKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdsb2dGb2xkQ2hhbmdlJywgCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19tYXhfaGVpZ2h0ID0gdW5pdCgxMiwnY20nKSkKCgpyZWxhYmVsX25vbm5ldXJhbCRtaWNyb2dsaWEgPC0gYygzMCw1OSw4NiwgOTIpCnJlbGFiZWxfbm9ubmV1cmFsJGB0L25rYCA8LSBjKDE2LDExMikKcmVsYWJlbF9ub25uZXVyYWwkYGJgIDwtIGMoNzgpCnJlbGFiZWxfbm9ubmV1cmFsJG1hY3JvcGhhZ2UgPC0gYygzNSw1MCw3NSkKcmVsYWJlbF9ub25uZXVyYWwkbHltb3Bob2N5dGUgPC0gYygxMTApCnJlbGFiZWxfbm9ubmV1cmFsJG1hc3QgPC0gYygxMTcpCnN1c19ub25uZXVyYWwkYHJlZCBibG9vZGAgPC0gYygxMjIpCm9ic19ub25uZXVyYWwkbGFiZWxzICU+JSBmaWx0ZXIobGVpZGVuMyAlaW4lIGMoaW1tdW5lX2NsdXN0ZXJzKSkgJT4lIGRhdGEuZnJhbWUgJT4lIHNlbGVjdChsZWlkZW4zLCBtTUNULCBUb3RhbENvdW50LCBzdWJDVCwgc3ViQ291bnQpICU+JSBEVDo6ZGF0YXRhYmxlKCkKCnAgPC0gZ2d0cmVlKGhjbHVzdF9zaW0pCnAkZGF0YSA8LSBwJGRhdGEgJT4lIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscyAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShub3RlID0gY2FzZV93aGVuKGxlaWRlbjMgJWluJSBjKGltbXVuZV9jbHVzdGVycywxMDQsMTIzKSB+ICdpbW11bmUnKSksIGJ5ID0gYygibGFiZWwiID0gImxlaWRlbjMiKSkgCgpwICsgbGF5b3V0X2RlbmRyb2dyYW0oKSArCiAgZ2VvbV90aXBsYWIoYWVzKGxhYmVsID0gcGFzdGUobGFiZWwsIG1NQ1QsIG5vdGUsIHNlcCA9ICcgLSAnKSwgY29sb3IgPSBtQ1QpKSArIAogIGdlb21fdGlwcG9pbnQoYWVzKHNoYXBlID0gbm90ZSksIHNpemU9IDMpICsKICB0aGVtZV9kZW5kcm9ncmFtKHBsb3QubWFyZ2luPW1hcmdpbigxNiwxNiwzMDAsMTYpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArIAogIGd1aWRlcyhjb2xvcj0ibm9uZSIpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OH0Kb2JzX25vbm5ldXJhbCRvYnMgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBsZWZ0X2pvaW4ocmVsYWJlbF9ub25uZXVyYWwgJT4lIGVuZnJhbWUobmFtZSA9ICduZXdDVCcsIHZhbHVlID0gJ2xlaWRlbjMnKSAlPiUgdW5uZXN0KGxlaWRlbjMpLCAKICAgICAgICAgICAgYnkgPSAnbGVpZGVuMycpICU+JSAKICBtdXRhdGUobUNUID0gY2FzZV93aGVuKCFpcy5uYShuZXdDVCkgfiBuZXdDVCwKICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBtQ1QpKSAlPiUgCiAgZmlsdGVyKGxlaWRlbjMgJWluJSBpbW11bmVfY2x1c3RlcnMpICU+JSAKICBnZ3Bsb3QoYWVzKHg9dW1hcDEseT11bWFwMikpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShhZXMoY29sb3IgPSBtQ1QpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGRhdGEgPSAuICU+JSBmaWx0ZXIobGVpZGVuMyAlaW4lIGMoc3VzX25vbm5ldXJhbCRtZWxhbm9jeXRlICkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gJ3JlZCcsIHBvaW50c2l6ZSA9IDAuOCwgYWxwaGEgPSAxKSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gLiAlPiUgZ3JvdXBfYnkobGVpZGVuMywgbUNUKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bWFwMiA9IG1lZGlhbih1bWFwMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gbGVpZGVuMyksIG1heC5vdmVybGFwcyA9IEluZikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmdsYXNiZXkoKSwgcGFsczo6Z2xhc2JleSgpKSAlPiUgdW5uYW1lKCkpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpIApgYGAKCgojIyBwcGUgLyBhcGUgLyBwY2UgLyBucGNlIC9jaWxpYXJ5IGJvZHkKCmFsbCBlcGl0aGVsaXVtCgpwcGUgPSBwb3N0ZXJpYXIgcGlnbWVudGVkIGVwaQphcGUgPSBhbnRlcmlvcgoKcGNlID0gcGlnbWVudGVkIGNpbGlhcnkKbnBjZSA9IG5vbnBpZ21lbnRlZAoKVGhlIGNpbGlhcnkgYm9keSBjZWxscyBmcm9tIFNSUDI1NTAxMiBhcmUgc2VlbWluZ2x5IGEgY29tYm8gb2YgcGlnbWVudGVkIGFuZCB1bnBpZ21lbnRlZCBlcGl0aGVsaXVtIChjbHVzdGVycyAxMjAsIDkwKS4gVGhlIFBDRSBOUENFIGFyZSBpbiBkaXN0aW5jdCBjbHVzdGVycyBmcm9tIFNSUDM2NDkxNS4gVGhvdWdoIHNvbWUgb2YgdGhlbSBmYWlsIHRvIHNob3cgdGhlIG1hcmtlcnMgdGhhdCB2YW4gWnlsIGV0IGFsIHVzZSwgc28gcGVyaGFwcyB0aGVzZSBhcmUgbG93ZXIgcXVhbGl0eSBjZWxscz8gTWFya2luZyB0aG9zZSBmb3IgcmVtb3ZhbC4gU2FtZSBmb3IgUFBFIC8gQVBFLiAKCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9CnRpYiA8LSBkaWZmX25vbm5ldXJhbCRkaWZmX3Rlc3RpbmcgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSBjKCdiYXNlJz0nbGVpZGVuMycpKSAlPiUgCiAgI2ZpbHRlcihncmVwbCgic2Nod2EiLG1NQ1QpKSAlPiUgCiAgbGVmdF9qb2luKGNvbnZfdGFibGUgJT4lIHNlbGVjdChTWU1CT0wsRU5TRU1CTCkgJT4lIHVuaXF1ZSgpKSAlPiUgCiAgZmlsdGVyKFNZTUJPTCAlaW4lIGMoIkFUUDZWMUMyIiwiQ0NCRTEiLCJMUlAyIiwiQ09MOUExIiwgIkhUUjJDIiwiTUVDT00iLAogICAgICAgICAgICAgICAgICAgICAgICJJR0ZOMSIsIlNMQzdBMiIsICJHQUxOVDE0IiwKICAgICAgICAgICAgICAgICAgICAgICAiRENUIiwgIlRZUiIsIk1MQU5BIikpICU+JSAKICBmaWx0ZXIoZ3JlcGwoInBwZXxhcGV8Y2lsaWFyeXxwY2UiLCBtTUNUKSkgJT4lIAogICMiQ0Q2OCIsIkNEMTQiLCAiR0FQREgiLCJQR0FNMSIpKSAlPiUgIywgI21hY3JvcGhhZ2UpKSAlPiUgCiAgbXV0YXRlKGJhc2UgPSBhcy5mYWN0b3IoYmFzZSkpICU+JSAKICBtdXRhdGUoYmFzZSA9IGNhc2Vfd2hlbihncmVwbCgicHBlfGFwZXxjaWxpYXJ5fHBjZSIsIG1NQ1QpIH4gcGFzdGUwKGJhc2UsICcgLSAnLCBtTUNUKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gYmFzZSkpICU+JSAKICBzZWxlY3QoU1lNQk9MLCBiYXNlLCBsb2dmb2xkY2hhbmdlcykgJT4lIAogIHBpdm90X3dpZGVyKHZhbHVlc19mcm9tID0gbG9nZm9sZGNoYW5nZXMsIG5hbWVzX2Zyb20gPSBiYXNlKQoKbWF0IDwtIHRpYiAlPiUgc2VsZWN0KC0xKSAlPiUgYXMubWF0cml4KCkKcm93Lm5hbWVzKG1hdCkgPC0gdGliICU+JSBwdWxsKDEpCgpDVCA9IGNvbG5hbWVzKHRpYilbLTFdICU+JSAKICBnc3ViKCdcXGQrIC0gJywnJywgLikgCgojbmFtZXMoQ1QpIDwtIGMocGFsczo6YWxwaGFiZXQoKSxwYWxzOjpnbGFzYmV5KCkpCgpoYV9jb2x1bW4gPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKAogIGRmID0gCiAgICBkYXRhLmZyYW1lKENUKQopCgpjb2xfZnVuID0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygtNiwgMCwgNiksIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCkNvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKG1hdCwgY29sPWNvbF9mdW4sCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAnbG9nRm9sZENoYW5nZScsIAogICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZXNfbWF4X2hlaWdodCA9IHVuaXQoMTIsJ2NtJykpCgoKb2JzX25vbm5ldXJhbCRsYWJlbHMgJT4lIGZpbHRlcihncmVwbCgicHBlfGFwZXxjaWxpYXJ5fHBjZSIsIG1NQ1QpKSAlPiUgZGF0YS5mcmFtZSAlPiUgc2VsZWN0KGxlaWRlbjMsIG1NQ1QsIFRvdGFsQ291bnQsIHN1YkNULCBzdWJDVF9SYXRpbywgc3ViQ291bnQpICU+JSBEVDo6ZGF0YXRhYmxlKCkKCnAgPC0gZ2d0cmVlKGhjbHVzdF9zaW0pCnAkZGF0YSA8LSBwJGRhdGEgJT4lIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscyAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShub3RlID0gY2FzZV93aGVuKGdyZXBsKCJwcGV8YXBlfGNpbGlhcnl8cGNlfG11c2NsZSIsIG1NQ1QpIH4gJ2UnKSksIGJ5ID0gYygibGFiZWwiID0gImxlaWRlbjMiKSkgCgpwICsgbGF5b3V0X2RlbmRyb2dyYW0oKSArCiAgZ2VvbV90aXBsYWIoYWVzKGxhYmVsID0gcGFzdGUobGFiZWwsIG1NQ1QsIG5vdGUsIHNlcCA9ICcgLSAnKSwgY29sb3IgPSBtQ1QpKSArIAogIGdlb21fdGlwcG9pbnQoYWVzKHNoYXBlID0gbm90ZSksIHNpemU9IDMpICsKICB0aGVtZV9kZW5kcm9ncmFtKHBsb3QubWFyZ2luPW1hcmdpbigxNiwxNiwzMDAsMTYpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArIAogIGd1aWRlcyhjb2xvcj0ibm9uZSIpCgpzdXNfbm9ubmV1cmFsJHBjZSA8LSBjKDI0KQpzdXNfbm9ubmV1cmFsJG5wY2UgPC0gYyg4Miw2OCwgNjcpCnN1c19ub25uZXVyYWwkYXBlIDwtIGMoMjApCnN1c19ub25uZXVyYWwkcHBlIDwtIGMoMTksNjEpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OH0Kb2JzX25vbm5ldXJhbCRvYnMgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBsZWZ0X2pvaW4ocmVsYWJlbF9ub25uZXVyYWwgJT4lIGVuZnJhbWUobmFtZSA9ICduZXdDVCcsIHZhbHVlID0gJ2xlaWRlbjMnKSAlPiUgdW5uZXN0KGxlaWRlbjMpLCAKICAgICAgICAgICAgYnkgPSAnbGVpZGVuMycpICU+JSAKICBtdXRhdGUobUNUID0gY2FzZV93aGVuKCFpcy5uYShuZXdDVCkgfiBuZXdDVCwKICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBtQ1QpKSAlPiUgCiAgZmlsdGVyKGdyZXBsKCJwcGV8YXBlfHBjZXxjaWxpYXJ5IixtTUNUKSkgJT4lIAogIGdncGxvdChhZXMoeD11bWFwMSx5PXVtYXAyKSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGFlcyhjb2xvciA9IG1DVCksIHBvaW50c2l6ZSA9IDAuOCwgYWxwaGEgPSAxKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoZGF0YSA9IC4gJT4lIGZpbHRlcihsZWlkZW4zICVpbiUgYyhzdXNfbm9ubmV1cmFsJHBwZSwgc3VzX25vbm5ldXJhbCRwY2UsIHN1c19ub25uZXVyYWwkbnBjZSwgc3VzX25vbm5ldXJhbCRhcGUgKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAncmVkJywgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDEpICsKICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSAuICU+JSBncm91cF9ieShsZWlkZW4zLCBtQ1QpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHVtYXAxID0gbWVkaWFuKHVtYXAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBsZWlkZW4zKSwgbWF4Lm92ZXJsYXBzID0gSW5mKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKClbMjoxMF0sIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSAKYGBgCgoKIyMgcGVyaWN5dGUKCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9Nn0KdGliIDwtIGRpZmZfbm9ubmV1cmFsJGRpZmZfdGVzdGluZyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9IGMoJ2Jhc2UnPSdsZWlkZW4zJykpICU+JSAKICAjZmlsdGVyKGdyZXBsKCJzY2h3YSIsbU1DVCkpICU+JSAKICBsZWZ0X2pvaW4oY29udl90YWJsZSAlPiUgc2VsZWN0KFNZTUJPTCxFTlNFTUJMKSAlPiUgdW5pcXVlKCkpICU+JSAKICBmaWx0ZXIoU1lNQk9MICVpbiUgYygiTk9UQ0gzIiwiUERHRlJCIiwiTUNBTSIsIkhJR0QxQiIsICJBRENZMyIpKSAlPiUgCiAgIyJDRDY4IiwiQ0QxNCIsICJHQVBESCIsIlBHQU0xIikpICU+JSAjLCAjbWFjcm9waGFnZSkpICU+JSAKICBtdXRhdGUoYmFzZSA9IGFzLmZhY3RvcihiYXNlKSkgJT4lIAogIG11dGF0ZShiYXNlID0gY2FzZV93aGVuKGdyZXBsKCJwZXJpY3l0ZSIsIG1NQ1QpIH4gcGFzdGUwKGJhc2UsICcgLSAnLCBtTUNUKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gYmFzZSkpICU+JSAKICBzZWxlY3QoU1lNQk9MLCBiYXNlLCBsb2dmb2xkY2hhbmdlcykgJT4lIAogIHBpdm90X3dpZGVyKHZhbHVlc19mcm9tID0gbG9nZm9sZGNoYW5nZXMsIG5hbWVzX2Zyb20gPSBiYXNlKQoKbWF0IDwtIHRpYiAlPiUgc2VsZWN0KC0xKSAlPiUgYXMubWF0cml4KCkKcm93Lm5hbWVzKG1hdCkgPC0gdGliICU+JSBwdWxsKDEpCgpDVCA9IGNvbG5hbWVzKHRpYilbLTFdICU+JSAKICBnc3ViKCdcXGQrIC0gJywnJywgLikgCgojbmFtZXMoQ1QpIDwtIGMocGFsczo6YWxwaGFiZXQoKSxwYWxzOjpnbGFzYmV5KCkpCgpoYV9jb2x1bW4gPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKAogIGRmID0gCiAgICBkYXRhLmZyYW1lKENUKQopCgpjb2xfZnVuID0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygtNiwgMCwgNiksIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCkNvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKG1hdCwgY29sPWNvbF9mdW4sCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAnbG9nRm9sZENoYW5nZScsIAogICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZXNfbWF4X2hlaWdodCA9IHVuaXQoMTIsJ2NtJykpCgoKb2JzX25vbm5ldXJhbCRsYWJlbHMgJT4lIGZpbHRlcihncmVwbCgicGVyaWN5dGUiLCBtTUNUKSkgJT4lIGRhdGEuZnJhbWUgJT4lIHNlbGVjdChsZWlkZW4zLCBtTUNULCBUb3RhbENvdW50LCBzdWJDVCwgc3ViQ1RfUmF0aW8sIHN1YkNvdW50KSAlPiUgRFQ6OmRhdGF0YWJsZSgpCgpwIDwtIGdndHJlZShoY2x1c3Rfc2ltKQpwJGRhdGEgPC0gcCRkYXRhICU+JSBsZWZ0X2pvaW4ob2JzX25vbm5ldXJhbCRsYWJlbHMgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUobm90ZSA9IGNhc2Vfd2hlbihncmVwbCgicGVyaWN5dGUiLCBtTUNUKSB+ICdlJykpLCBieSA9IGMoImxhYmVsIiA9ICJsZWlkZW4zIikpIAoKcCArIGxheW91dF9kZW5kcm9ncmFtKCkgKwogIGdlb21fdGlwbGFiKGFlcyhsYWJlbCA9IHBhc3RlKGxhYmVsLCBtTUNULCBub3RlLCBzZXAgPSAnIC0gJyksIGNvbG9yID0gbUNUKSkgKyAKICBnZW9tX3RpcHBvaW50KGFlcyhzaGFwZSA9IG5vdGUpLCBzaXplPSAzKSArCiAgdGhlbWVfZGVuZHJvZ3JhbShwbG90Lm1hcmdpbj1tYXJnaW4oMTYsMTYsMzAwLDE2KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjpnbGFzYmV5KCkpICU+JSB1bm5hbWUoKSkgKyAKICBndWlkZXMoY29sb3I9Im5vbmUiKQoKcmVsYWJlbF9ub25uZXVyYWwkcGVyaWN5dGUgPC0gYyg4MSwzMiwxMjIpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OH0Kb2JzX25vbm5ldXJhbCRvYnMgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBsZWZ0X2pvaW4ocmVsYWJlbF9ub25uZXVyYWwgJT4lIGVuZnJhbWUobmFtZSA9ICduZXdDVCcsIHZhbHVlID0gJ2xlaWRlbjMnKSAlPiUgdW5uZXN0KGxlaWRlbjMpLCAKICAgICAgICAgICAgYnkgPSAnbGVpZGVuMycpICU+JSAKICBtdXRhdGUobUNUID0gY2FzZV93aGVuKCFpcy5uYShuZXdDVCkgfiBuZXdDVCwKICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBtQ1QpKSAlPiUgCiAgZmlsdGVyKGxlaWRlbjMgJWluJSByZWxhYmVsX25vbm5ldXJhbCRwZXJpY3l0ZSkgJT4lIAogIGdncGxvdChhZXMoeD11bWFwMSx5PXVtYXAyKSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGFlcyhjb2xvciA9IG1DVCksIHBvaW50c2l6ZSA9IDAuOCwgYWxwaGEgPSAxKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoZGF0YSA9IC4gJT4lIGZpbHRlcihsZWlkZW4zICVpbiUgYyhzdXNfbm9ubmV1cmFsJHBwZSwgc3VzX25vbm5ldXJhbCRwY2UsIHN1c19ub25uZXVyYWwkbnBjZSwgc3VzX25vbm5ldXJhbCRhcGUgKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAncmVkJywgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDEpICsKICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSAuICU+JSBncm91cF9ieShsZWlkZW4zLCBtQ1QsIHRpc3N1ZXMpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHVtYXAxID0gbWVkaWFuKHVtYXAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSB0aXNzdWVzKSwgbWF4Lm92ZXJsYXBzID0gSW5mKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKClbMjoxMF0sIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSAKYGBgCgojIyBsZW5zCgpDbHVzdGVyIDcgaXMgY29tcHJpc2VkIG9mIG1vc3Qgb2YgdGhlIGxlbnMgY2VsbHMgKGh0dHBzOi8vd3d3LnBuYXMub3JnL2RvaS9mdWxsLzEwLjEwNzMvcG5hcy4yMjAwOTE0MTE5LCBmaWcgNCkKCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9Nn0KdGliIDwtIGRpZmZfbm9ubmV1cmFsJGRpZmZfdGVzdGluZyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9IGMoJ2Jhc2UnPSdsZWlkZW4zJykpICU+JSAKICAjZmlsdGVyKGdyZXBsKCJzY2h3YSIsbU1DVCkpICU+JSAKICBsZWZ0X2pvaW4oY29udl90YWJsZSAlPiUgc2VsZWN0KFNZTUJPTCxFTlNFTUJMKSAlPiUgdW5pcXVlKCkpICU+JSAKICBmaWx0ZXIoU1lNQk9MICVpbiUgYygiS0lGMjZCIiwiQk5JUDMiKSkgJT4lIAogICMiQ0Q2OCIsIkNEMTQiLCAiR0FQREgiLCJQR0FNMSIpKSAlPiUgIywgI21hY3JvcGhhZ2UpKSAlPiUgCiAgbXV0YXRlKGJhc2UgPSBhcy5mYWN0b3IoYmFzZSkpICU+JSAKICBtdXRhdGUoYmFzZSA9IGNhc2Vfd2hlbihncmVwbCgiZmliZXIiLCBtTUNUKSB+IHBhc3RlMChiYXNlLCAnIC0gJywgbU1DVCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IGJhc2UpKSAlPiUgCiAgc2VsZWN0KFNZTUJPTCwgYmFzZSwgbG9nZm9sZGNoYW5nZXMpICU+JSAKICBwaXZvdF93aWRlcih2YWx1ZXNfZnJvbSA9IGxvZ2ZvbGRjaGFuZ2VzLCBuYW1lc19mcm9tID0gYmFzZSkKCm1hdCA8LSB0aWIgJT4lIHNlbGVjdCgtMSkgJT4lIGFzLm1hdHJpeCgpCnJvdy5uYW1lcyhtYXQpIDwtIHRpYiAlPiUgcHVsbCgxKQoKQ1QgPSBjb2xuYW1lcyh0aWIpWy0xXSAlPiUgCiAgZ3N1YignXFxkKyAtICcsJycsIC4pIAoKI25hbWVzKENUKSA8LSBjKHBhbHM6OmFscGhhYmV0KCkscGFsczo6Z2xhc2JleSgpKQoKaGFfY29sdW1uIDwtIENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwQW5ub3RhdGlvbigKICBkZiA9IAogICAgZGF0YS5mcmFtZShDVCkKKQoKY29sX2Z1biA9IGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTYsIDAsIDYpLCBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQpDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChtYXQsIGNvbD1jb2xfZnVuLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ2xvZ0ZvbGRDaGFuZ2UnLCAKICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWVzX21heF9oZWlnaHQgPSB1bml0KDEyLCdjbScpKQoKCm9ic19ub25uZXVyYWwkbGFiZWxzICU+JSBmaWx0ZXIoZ3JlcGwoImZpYmVyIiwgbU1DVCkpICU+JSBkYXRhLmZyYW1lICU+JSBzZWxlY3QobGVpZGVuMywgbU1DVCwgVG90YWxDb3VudCwgc3ViQ1QsIHN1YkNUX1JhdGlvLCBzdWJDb3VudCkgJT4lIERUOjpkYXRhdGFibGUoKQoKcCA8LSBnZ3RyZWUoaGNsdXN0X3NpbSkKcCRkYXRhIDwtIHAkZGF0YSAlPiUgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKG5vdGUgPSBjYXNlX3doZW4oZ3JlcGwoImZpYmVyIiwgbU1DVCkgfiAnZScpKSwgYnkgPSBjKCJsYWJlbCIgPSAibGVpZGVuMyIpKSAKCnAgKyBsYXlvdXRfZGVuZHJvZ3JhbSgpICsKICBnZW9tX3RpcGxhYihhZXMobGFiZWwgPSBwYXN0ZShsYWJlbCwgbU1DVCwgbm90ZSwgc2VwID0gJyAtICcpLCBjb2xvciA9IG1DVCkpICsgCiAgZ2VvbV90aXBwb2ludChhZXMoc2hhcGUgPSBub3RlKSwgc2l6ZT0gMykgKwogIHRoZW1lX2RlbmRyb2dyYW0ocGxvdC5tYXJnaW49bWFyZ2luKDE2LDE2LDMwMCwxNikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpKSAlPiUgdW5uYW1lKCkpICsgCiAgZ3VpZGVzKGNvbG9yPSJub25lIikKCnJlbGFiZWxfbm9ubmV1cmFsJGxlbnMgPC0gYyg3KQpgYGAKCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTh9Cm9ic19ub25uZXVyYWwkb2JzICU+JSAKICBsZWZ0X2pvaW4ob2JzX25vbm5ldXJhbCRsYWJlbHMsIGJ5ID0gJ2xlaWRlbjMnKSAlPiUgCiAgbGVmdF9qb2luKHJlbGFiZWxfbm9ubmV1cmFsICU+JSBlbmZyYW1lKG5hbWUgPSAnbmV3Q1QnLCB2YWx1ZSA9ICdsZWlkZW4zJykgJT4lIHVubmVzdChsZWlkZW4zKSwgCiAgICAgICAgICAgIGJ5ID0gJ2xlaWRlbjMnKSAlPiUgCiAgbXV0YXRlKG1DVCA9IGNhc2Vfd2hlbighaXMubmEobmV3Q1QpIH4gbmV3Q1QsCiAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gbUNUKSkgJT4lIAogIGZpbHRlcihsZWlkZW4zICVpbiUgcmVsYWJlbF9ub25uZXVyYWwkcGVyaWN5dGUpICU+JSAKICBnZ3Bsb3QoYWVzKHg9dW1hcDEseT11bWFwMikpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShhZXMoY29sb3IgPSBtQ1QpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGRhdGEgPSAuICU+JSBmaWx0ZXIobGVpZGVuMyAlaW4lIGMoc3VzX25vbm5ldXJhbCRwcGUsIHN1c19ub25uZXVyYWwkcGNlLCBzdXNfbm9ubmV1cmFsJG5wY2UsIHN1c19ub25uZXVyYWwkYXBlICkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gJ3JlZCcsIHBvaW50c2l6ZSA9IDAuOCwgYWxwaGEgPSAxKSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gLiAlPiUgZ3JvdXBfYnkobGVpZGVuMywgbUNUKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bWFwMiA9IG1lZGlhbih1bWFwMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gbGVpZGVuMyksIG1heC5vdmVybGFwcyA9IEluZikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmFscGhhYmV0MigpWzI6MTBdLCBwYWxzOjpnbGFzYmV5KCkpICU+JSB1bm5hbWUoKSkgKyAKICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgCmBgYAoKCiMjIChzbW9vdGgpIG11c2NsZSwgc3BoaW5jdGVyIChpcmlzIG11c2NsZSkKCkFsbCBhcmUgZmluZSBhcyBpcyAobUNUKQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTZ9CnRpYiA8LSBkaWZmX25vbm5ldXJhbCRkaWZmX3Rlc3RpbmcgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSBjKCdiYXNlJz0nbGVpZGVuMycpKSAlPiUgCiAgI2ZpbHRlcihncmVwbCgic2Nod2EiLG1NQ1QpKSAlPiUgCiAgbGVmdF9qb2luKGNvbnZfdGFibGUgJT4lIHNlbGVjdChTWU1CT0wsRU5TRU1CTCkgJT4lIHVuaXF1ZSgpKSAlPiUgCiAgI2ZpbHRlcihTWU1CT0wgJWluJSBjKCJDSFJNMyIsIkRFUyIsIk1ZSDExIiwgJ1BQUDFSMTJBJywnQUNUQTInLCdDTk4xJywnVEFHTE4nKSkgJT4lIAogIGZpbHRlcihTWU1CT0wgJWluJSBjKCJQUFAxUjEyQSIsICAgICJBVFAyQTIiLCAgICAgICJDSFJNMyIsICAgICAgICJQREUzQSIsICJBQ1RBMiIsICJERVMiLCAiQURSQTFBIiwgIlRQTTIiLCAiTVlPQyIsCiAgICAgICAgICAgICAgICAgICAgICAgIkdMSVMxIiwgIkNIUk0zIiwiVFBNMSIsJ0NPTDRBMicsICdJUkFHMScsIk1ZSDExIiwgIklSQUcxIiwiU1Q2R0FMTkFDNSIpKSAlPiUgICMgcGVyaWN5dGUKICMgZmlsdGVyKFNZTUJPTCAlaW4lIHkpICU+JSAKICBtdXRhdGUoYmFzZSA9IGFzLmZhY3RvcihiYXNlKSkgJT4lIAogIG11dGF0ZShiYXNlID0gY2FzZV93aGVuKGdyZXBsKCJtdXNjbGV8c3BoaW5jIiwgbU1DVCkgfiBwYXN0ZTAoYmFzZSwgJyAtICcsIG1NQ1QpLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBiYXNlKSkgJT4lIAogIHNlbGVjdChTWU1CT0wsIGJhc2UsIGxvZ2ZvbGRjaGFuZ2VzKSAlPiUgCiAgcGl2b3Rfd2lkZXIodmFsdWVzX2Zyb20gPSBsb2dmb2xkY2hhbmdlcywgbmFtZXNfZnJvbSA9IGJhc2UpCgptYXQgPC0gdGliICU+JSBzZWxlY3QoLTEpICU+JSBhcy5tYXRyaXgoKQpyb3cubmFtZXMobWF0KSA8LSB0aWIgJT4lIHB1bGwoMSkKCkNUID0gY29sbmFtZXModGliKVstMV0gJT4lIAogIGdzdWIoJ1xcZCsgLSAnLCcnLCAuKSAKCiNuYW1lcyhDVCkgPC0gYyhwYWxzOjphbHBoYWJldCgpLHBhbHM6OmdsYXNiZXkoKSkKCmhhX2NvbHVtbiA8LSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oCiAgZGYgPQogICAgZGF0YS5mcmFtZShDVCkKKQoKY29sX2Z1biA9IGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTYsIDAsIDYpLCBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQpDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChtYXQsIGNvbD1jb2xfZnVuLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ2xvZ0ZvbGRDaGFuZ2UnLCByb3dfbmFtZXNfc2lkZSA9ICdsZWZ0JywKICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWVzX21heF9oZWlnaHQgPSB1bml0KDEyLCdjbScpKQoKCm9ic19ub25uZXVyYWwkbGFiZWxzICU+JSBmaWx0ZXIoZ3JlcGwoIm11c2NsZSIsIG1NQ1QpKSAlPiUgZGF0YS5mcmFtZSAlPiUgc2VsZWN0KGxlaWRlbjMsIG1NQ1QsIFRvdGFsQ291bnQsIHN1YkNULCBzdWJDVF9SYXRpbywgc3ViQ291bnQpICU+JSBEVDo6ZGF0YXRhYmxlKCkKCnAgPC0gZ2d0cmVlKGhjbHVzdF9zaW0pCnAkZGF0YSA8LSBwJGRhdGEgJT4lIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscyAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKG5vdGUgPSBjYXNlX3doZW4oZ3JlcGwoIm11c2NsZSIsIG1NQ1QpIH4gJ211c2NsZScpKSwgYnkgPSBjKCJsYWJlbCIgPSAibGVpZGVuMyIpKQoKcCArIGxheW91dF9kZW5kcm9ncmFtKCkgKwogIGdlb21fdGlwbGFiKGFlcyhsYWJlbCA9IHBhc3RlKGxhYmVsLCBtTUNULCBub3RlLCBzZXAgPSAnIC0gJyksIGNvbG9yID0gbUNUKSkgKwogIGdlb21fdGlwcG9pbnQoYWVzKHNoYXBlID0gbm90ZSksIHNpemU9IDMpICsKICB0aGVtZV9kZW5kcm9ncmFtKHBsb3QubWFyZ2luPW1hcmdpbigxNiwxNiwzMDAsMTYpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArCiAgZ3VpZGVzKGNvbG9yPSJub25lIikKCiMgc2ltcGxpZnkgbXVzY2xlIGxhYmVsbGluZwpyZWxhYmVsX25vbm5ldXJhbCRtdXNjbGUgPC0gYygxMCwyNyw3Miw3NCw5Myw5NCw5OSkKaHJfbm9ubmV1cmFsJGBtdXNjbGUgKGNpbGlhcnkpYCA8LSBjKDEwLDI3KQpocl9ub25uZXVyYWwkYG11c2NsZSAoc21vb3RoKWAgPC0gYyg3Miw3NCw5OSkKaHJfbm9ubmV1cmFsJGBtdXNjbGUgKGlyaXMgZGlsYXRvcilgIDwtIGMoOTMpCmhyX25vbm5ldXJhbCRgbXVzY2xlIChpcmlzIHNwaGluY3RlcilgIDwtIGMoOTQpCgpgYGAKCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTh9Cm9ic19ub25uZXVyYWwkb2JzICU+JSAKICBsZWZ0X2pvaW4ob2JzX25vbm5ldXJhbCRsYWJlbHMsIGJ5ID0gJ2xlaWRlbjMnKSAlPiUgCiAgZmlsdGVyKG1DVCAlaW4lIGMoIm11c2NsZSIsInNtb290aCBtdXNjbGUiLCAic3BoaW5jdGVyIikpICU+JSAKICBsZWZ0X2pvaW4oaHJfbm9ubmV1cmFsICU+JWVuZnJhbWUobmFtZSA9ICdDVCcsIHZhbHVlID0gJ2xlaWRlbjMnKSAlPiUgdW5uZXN0KGxlaWRlbjMpKSAlPiUgCiAgZ2dwbG90KGFlcyh4PXVtYXAxLHk9dW1hcDIpKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoYWVzKGNvbG9yID0gQ1QpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMSkgKwogIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IC4gJT4lIGdyb3VwX2J5KGxlaWRlbjMsIENUKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bWFwMiA9IG1lZGlhbih1bWFwMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gbGVpZGVuMyksIG1heC5vdmVybGFwcyA9IEluZikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmFscGhhYmV0MigpWzI6MTBdLCBwYWxzOjpnbGFzYmV5KCkpICU+JSB1bm5hbWUoKSkgKyAKICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgCmBgYAoKIyMgb3BjIC8gb2xpZ28KClRoZXJlIGEgUENESDkrIG9saWdvZGVuZHJvY3l0ZSBhbmQgUENESDktIHBvcHVsYXRpb25zCgpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTZ9CnRpYiA8LSBkaWZmX25vbm5ldXJhbCRkaWZmX3Rlc3RpbmcgJT4lIAogIGxlZnRfam9pbihvYnNfbm9ubmV1cmFsJGxhYmVscywgYnkgPSBjKCdiYXNlJz0nbGVpZGVuMycpKSAlPiUgCiAgI2ZpbHRlcihncmVwbCgic2Nod2EiLG1NQ1QpKSAlPiUgCiAgbGVmdF9qb2luKGNvbnZfdGFibGUgJT4lIHNlbGVjdChTWU1CT0wsRU5TRU1CTCkgJT4lIHVuaXF1ZSgpKSAlPiUgCiAgZmlsdGVyKFNZTUJPTCAlaW4lIGMoIkdSSUE0IiwiQ1ROTkEzIiwiUENESDkiLCJMUlJUTTMiKSkgJT4lIAogICMiQ0Q2OCIsIkNEMTQiLCAiR0FQREgiLCJQR0FNMSIpKSAlPiUgIywgI21hY3JvcGhhZ2UpKSAlPiUgCiAgbXV0YXRlKGJhc2UgPSBhcy5mYWN0b3IoYmFzZSkpICU+JSAKICBtdXRhdGUoYmFzZSA9IGNhc2Vfd2hlbihncmVwbCgib3BjfG9saWdvIiwgbU1DVCkgfiBwYXN0ZTAoYmFzZSwgJyAtICcsIG1NQ1QpLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBiYXNlKSkgJT4lIAogIHNlbGVjdChTWU1CT0wsIGJhc2UsIGxvZ2ZvbGRjaGFuZ2VzKSAlPiUgCiAgcGl2b3Rfd2lkZXIodmFsdWVzX2Zyb20gPSBsb2dmb2xkY2hhbmdlcywgbmFtZXNfZnJvbSA9IGJhc2UpCgptYXQgPC0gdGliICU+JSBzZWxlY3QoLTEpICU+JSBhcy5tYXRyaXgoKQpyb3cubmFtZXMobWF0KSA8LSB0aWIgJT4lIHB1bGwoMSkKCkNUID0gY29sbmFtZXModGliKVstMV0gJT4lIAogIGdzdWIoJ1xcZCsgLSAnLCcnLCAuKSAKCiNuYW1lcyhDVCkgPC0gYyhwYWxzOjphbHBoYWJldCgpLHBhbHM6OmdsYXNiZXkoKSkKCmhhX2NvbHVtbiA8LSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oCiAgZGYgPSAKICAgIGRhdGEuZnJhbWUoQ1QpCikKCmNvbF9mdW4gPSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC02LCAwLCA2KSwgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKQ29tcGxleEhlYXRtYXA6OkhlYXRtYXAobWF0LCBjb2w9Y29sX2Z1biwKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdsb2dGb2xkQ2hhbmdlJywgCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19tYXhfaGVpZ2h0ID0gdW5pdCgxMiwnY20nKSkKCgpvYnNfbm9ubmV1cmFsJGxhYmVscyAlPiUgZmlsdGVyKGdyZXBsKCJvcGN8b2xpZ28iLCBtTUNUKSkgJT4lIGRhdGEuZnJhbWUgJT4lIHNlbGVjdChsZWlkZW4zLCBtTUNULCBUb3RhbENvdW50LCBzdWJDVCwgc3ViQ1RfUmF0aW8sIHN1YkNvdW50KSAlPiUgRFQ6OmRhdGF0YWJsZSgpCgpocl9ub25uZXVyYWwkYG9saWdvZGVuZHJvY3l0ZSAoUENESDkrKWAgPC0gYygxMiwzNikKaHJfbm9ubmV1cmFsJGBvbGlnb2RlbmRyb2N5dGUgKFBDREg5LSlgIDwtIGMoMCkKYGBgCgoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04fQpvYnNfbm9ubmV1cmFsJG9icyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9ICdsZWlkZW4zJykgJT4lIAogIGZpbHRlcihtQ1QgJWluJSBjKCJvcGMiLCJvbGlnbyIpKSAlPiUgCiAgZ2dwbG90KGFlcyh4PXVtYXAxLHk9dW1hcDIpKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoYWVzKGNvbG9yID0gbUNUKSwgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDEpICsKICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSAuICU+JSBncm91cF9ieShsZWlkZW4zLCBtQ1QpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHVtYXAxID0gbWVkaWFuKHVtYXAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBsZWlkZW4zKSwgbWF4Lm92ZXJsYXBzID0gSW5mKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKClbMjoxMF0sIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSAKYGBgCgojIE5FVyAKClVwZGF0ZSBvdmVyYWxsIGdyYXBoaWNzIG9uIHRoZSBuZXcgbGFiZWxzCgojIyBVTUFQCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OX0KcmVsYWJlbF9ub25uZXVyYWxfbG9uZyA8LSByZWxhYmVsX25vbm5ldXJhbCAlPiUgZW5mcmFtZShuYW1lID0gJ25ld0NUJywgdmFsdWUgPSAnbGVpZGVuMycpICU+JSB1bm5lc3QobGVpZGVuMykgCmhyX2xvbmcgPC0gaHJfbm9ubmV1cmFsICU+JSBlbmZyYW1lKG5hbWUgPSAnaHJDVCcsIHZhbHVlID0gJ2xlaWRlbjMnKSAlPiUgdW5uZXN0KGxlaWRlbjMpIApzdXNfbm9ubmV1cmFsX2xvbmcgPC0gc3VzX25vbm5ldXJhbCAlPiUgZW5mcmFtZShuYW1lID0gJ25ld0NUJywgdmFsdWUgPSAnbGVpZGVuMycpICU+JSB1bm5lc3QobGVpZGVuMykgJT4lIAogIGZpbHRlcighbGVpZGVuMyAlaW4lIHJlbGFiZWxfbm9ubmV1cmFsX2xvbmckbGVpZGVuMykgJT4lIAogIGZpbHRlcighbGVpZGVuMyAlaW4lIGhyX2xvbmckbGVpZGVuMykKCm9ic19ub25uZXVyYWwkbm9icyA8LSBvYnNfbm9ubmV1cmFsJG9icyAlPiUgCiAgbGVmdF9qb2luKG9ic19ub25uZXVyYWwkbGFiZWxzLCBieSA9ICdsZWlkZW4zJykgJT4lIAogIGxlZnRfam9pbihyZWxhYmVsX25vbm5ldXJhbF9sb25nLCAKICAgICAgICAgICAgYnkgPSAnbGVpZGVuMycpICU+JQogIGxlZnRfam9pbihocl9sb25nLCAKICAgICAgICAgICAgYnkgPSAnbGVpZGVuMycpICU+JQogIGZpbHRlcighbGVpZGVuMyAlaW4lIChzdXNfbm9ubmV1cmFsX2xvbmckbGVpZGVuMykpICU+JSAKICBtdXRhdGUoQ1QgPSBjYXNlX3doZW4obUNUID09ICdiZWFtJyB+ICdmaWJyb2JsYXN0JywKICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKG5ld0NUKSB+IG5ld0NULAogICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gbUNUKSwKICAgICAgICAgaHJDVCA9IGNhc2Vfd2hlbighaXMubmEoaHJDVCkgfiBockNULAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBDVCkpCgpvYnNfbm9ubmV1cmFsJG5sYWJlbHMgPC0gb2JzX25vbm5ldXJhbCRsYWJlbHMgJT4lIAogIGxlZnRfam9pbihyZWxhYmVsX25vbm5ldXJhbF9sb25nLCAKICAgICAgICAgICAgYnkgPSAnbGVpZGVuMycpICU+JQogIGxlZnRfam9pbihocl9sb25nLCAKICAgICAgICAgICAgYnkgPSAnbGVpZGVuMycpICU+JQogIGZpbHRlcighbGVpZGVuMyAlaW4lIChzdXNfbm9ubmV1cmFsX2xvbmckbGVpZGVuMykpICU+JSAKICBtdXRhdGUoQ1QgPSBjYXNlX3doZW4obUNUID09ICdiZWFtJyB+ICdmaWJyb2JsYXN0JywKICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKG5ld0NUKSB+IG5ld0NULAogICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gbUNUKSwKICAgICAgICAgaHJDVCA9IGNhc2Vfd2hlbighaXMubmEoaHJDVCkgfiBockNULAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBDVCkpCm9ic19ub25uZXVyYWwkbm9icyAlPiUgCiAgZ2dwbG90KGFlcyh4PXVtYXAxLHk9dW1hcDIpKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoYWVzKGNvbG9yID0gQ1QpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMC41KSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gLiAlPiUgZ3JvdXBfYnkoQ1QpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHVtYXAxID0gbWVkaWFuKHVtYXAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBDVCwgY29sb3IgPSBDVCkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpKSAlPiUgdW5uYW1lKCkpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKb2JzX25vbm5ldXJhbCRub2JzICU+JSAKICBnZ3Bsb3QoYWVzKHg9dW1hcDEseT11bWFwMikpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShhZXMoY29sb3IgPSBhcy5mYWN0b3IoaHJDVCkpLCBwb2ludHNpemUgPSAyLjEsIGFscGhhID0gMC41KSArCiAgICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSAuICU+JSBncm91cF9ieShDVCwgaHJDVCkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UodW1hcDEgPSBtZWRpYW4odW1hcDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW1hcDIgPSBtZWRpYW4odW1hcDIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IGhyQ1QsIGNvbG9yID0gaHJDVCkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpLCBwYWxzOjphbHBoYWJldCgpLCBwYWxzOjpicmV3ZXIuc2V0MSg5KSwgcGFsczo6a2VsbHkoKSkgJT4lIHVubmFtZSgpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKyBmYWNldF93cmFwKH5DVCkKYGBgCgojIyBoY2x1c3QKYGBge3IsIGZpZy53aWR0aCA9IDE4LCBmaWcuaGVpZ2h0ID0gMTB9CnBiIDwtIGRhdGEudGFibGU6OmZyZWFkKCd+L2RhdGEvc2NFaWFEX21vZGVsaW5nL2hzMTExX21hdHVyZV9leWVfbm9ubmV1cmFsL2hzMTExX21hdHVyZV9leWVfMjAyNDEwMDFfZnVsbF9fTk9ObmV1cmFsMjAwMGh2Z18yMDBlXzMwbC5wc2V1ZG9CdWxrLmxlaWRlbjMuY3N2Lmd6JykKY29sbmFtZXMocGIpIDwtIGdzdWIoIlxcLlxcZCsiLCIiLGNvbG5hbWVzKHBiKSkKaHZnIDwtIGRhdGEudGFibGU6OmZyZWFkKCd+L2RhdGEvc2NFaWFEX21vZGVsaW5nL2hzMTExX21hdHVyZV9leWVfbm9ubmV1cmFsL2h2ZzIwMDAuY3N2Lmd6JylbLTEsXQpybmFtZXMgPC0gcGIkVjEKY2x1c3QgPC0gc3RyX2V4dHJhY3Qocm5hbWVzLCAnXFxkKycpICU+JSBhcy5pbnRlZ2VyKCkKcGIgPC0gcGJbLC0xXSAlPiUgYXMubWF0cml4KCkKcm93Lm5hbWVzKHBiKSA8LSBhcy5jaGFyYWN0ZXIoY2x1c3QpCnBiIDwtIHBiW2FzLmNoYXJhY3RlcihvYnNfbm9ubmV1cmFsJG5sYWJlbHMkbGVpZGVuMyksXQoKcGJfbm9ybSA8LSBtZXRhbW9ScGg6Om5vcm1hbGl6ZV9kYXRhKHQocGIpLCBzYW1wbGVfc2NhbGUgPSAnY3BtJykgJT4lIHQoKQoKcGJfbm9ybSA8LSBwYl9ub3JtWyxodmckVjJdCiNwYl9ub3JtIDwtIHBiX25vcm1bLGh2ZyRWMlshaHZnJFYyICVpbiUgYyhjY19nZW5lcyxyaWJvX2dlbmVzKV1dCiMgaHR0cHM6Ly9zdGF0cy5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvMzE1NjUvY29tcHV0ZS1hLWNvc2luZS1kaXNzaW1pbGFyaXR5LW1hdHJpeC1pbi1yCnNpbSA8LSBwYl9ub3JtIC8gc3FydChyb3dTdW1zKHBiX25vcm0gKiBwYl9ub3JtKSkKc2ltIDwtIHNpbSAlKiUgdChzaW0pCkRfc2ltIDwtIGFzLmRpc3QoMSAtIHNpbSkKCmhjbHVzdF9zaW0gPC0gaGNsdXN0KERfc2ltLCBtZXRob2QgPSAnYXZlcmFnZScpCgpoY2x1c3Rfc2ltJGxhYmVscyA8LSBvYnNfbm9ubmV1cmFsJG5sYWJlbHMgJT4lIHB1bGwobGVpZGVuMykKCmxpYnJhcnkoZ2d0cmVlKQpwIDwtIGdndHJlZShoY2x1c3Rfc2ltKQpwJGRhdGEgPC0gcCRkYXRhICU+JSBsZWZ0X2pvaW4ob2JzX25vbm5ldXJhbCRubGFiZWxzLCBieSA9IGMoImxhYmVsIiA9ICJsZWlkZW4zIikpICU+JQogIG11dGF0ZSh0ZWNoUmF0aW8gPSByb3VuZCh0ZWNoUmF0aW8sIGRpZ2l0cyA9IDIpKQpwICsgbGF5b3V0X2RlbmRyb2dyYW0oKSArCiAgZ2VvbV90aXBsYWIoYWVzKGxhYmVsID0gcGFzdGUobGFiZWwsIENULCBzdHVkeUNvdW50LCBUb3RhbENvdW50LCB0ZWNoUmF0aW8sIHNlcCA9ICcgLSAnKSwgY29sb3IgPSBDVCkpICsKICB0aGVtZV9kZW5kcm9ncmFtKHBsb3QubWFyZ2luPW1hcmdpbigxNiwxNiwzMDAsMTYpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArCiAgZ3VpZGVzKGNvbG9yPSJub25lIikKCgpwIDwtIGdndHJlZShoY2x1c3Rfc2ltKQpwJGRhdGEgPC0gcCRkYXRhICU+JSBsZWZ0X2pvaW4ob2JzX25vbm5ldXJhbCRubGFiZWxzICU+JSBtdXRhdGUoc3R1ZGllcyA9IGNhc2Vfd2hlbihzdHVkeUNvdW50ID09MSB+IHN0dWRpZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAibXVsdGlwbGUiKSksIGJ5ID0gYygibGFiZWwiID0gImxlaWRlbjMiKSkKCnAgKyBsYXlvdXRfZGVuZHJvZ3JhbSgpICsKICBnZW9tX3RpcGxhYihhZXMobGFiZWwgPSBwYXN0ZShsYWJlbCwgQ1QsIHN0dWRpZXMsIHNlcCA9ICcgLSAnKSwgY29sb3IgPSBDVCkpICsKICBnZW9tX3RpcHBvaW50KGFlcyhzaGFwZSA9IHN0dWRpZXMpLCBzaXplPSAzKSArCiAgdGhlbWVfZGVuZHJvZ3JhbShwbG90Lm1hcmdpbj1tYXJnaW4oMTYsMTYsMzAwLDE2KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjpnbGFzYmV5KCkpICU+JSB1bm5hbWUoKSkgKwogIGd1aWRlcyhjb2xvcj0ibm9uZSIpCgoKCgpgYGAKCgojIE91dHB1dHMKYGBge3J9CnNhdmUob2JzX25vbm5ldXJhbCwgZmlsZSA9ICdIdW1hbl9NYXR1cmVfRXllX2Z1bGxfX3N0YWdlNF9OT05uZXVyYWwub2JzLmZyZWV6ZTIwMjQxMTA3LlJkYXRhJykKb2JzX25vbm5ldXJhbCRub2JzICU+JSBzZWxlY3QoYmFyY29kZSwgbGVpZGVuMywgQ1QsIGhyQ1QpICU+JSB3cml0ZV9jc3YoJ0h1bWFuX01hdHVyZV9FeWVfZnVsbF9fc3RhZ2U0X05PTm5ldXJhbC5DVGNhbGxzLmZyZWV6ZTIwMjQxMTA3LmNzdi5neicpCmBgYA==